diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..2ed133b --- /dev/null +++ b/.dockerignore @@ -0,0 +1,8 @@ +node_modules +dist +bin +data +.git +.gitignore +*.log +.DS_Store diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..1f0953a --- /dev/null +++ b/.env.example @@ -0,0 +1,19 @@ +# 이 파일을 .env 로 복사한 뒤 값을 채워 쓰세요. (.env 는 .gitignore 로 제외됩니다.) +# 직접 실행(npm start) / Docker(compose) 양쪽에서 동일하게 사용됩니다. + +# 서버 바인딩 ───────────────────────────────────────────────────── +# Docker 컨테이너에선 반드시 0.0.0.0 으로 두세요. 직접 실행이고 외부에서 접근해야 하면 0.0.0.0. +# 호스트에서 로컬 전용으로만 쓰면 127.0.0.1 가능. +HOST=0.0.0.0 +PORT=3000 + +# 세션 비밀 ─────────────────────────────────────────────────────── +# 운영 시 반드시 충분히 긴 무작위 문자열로 바꿀 것. +SESSION_SECRET=change-me-please + +# 업로드/타임아웃 ───────────────────────────────────────────────── +# 업로드 용량 한도(바이트). 비우거나 미설정이면 무제한. +# UPLOAD_MAX_BYTES= + +# HTTP 요청 타임아웃(밀리초). 0 또는 미설정이면 무제한 (10GB 업로드 같은 케이스 대비). +# HTTP_REQUEST_TIMEOUT_MS=0 diff --git a/.gitignore b/.gitignore index b34c445..58993a8 100644 --- a/.gitignore +++ b/.gitignore @@ -9,5 +9,6 @@ data/jobs/* data/tmp/* !data/tmp/.gitkeep .env +!.env.example *.log .DS_Store diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..e8ed548 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,55 @@ +# node:22-bookworm-slim 으로 glibc 환경 유지 (yt-dlp_linux 가 glibc 바이너리). +# alpine 으로 가면 musl 충돌로 yt-dlp 실행 실패함. +FROM node:22-bookworm-slim AS build + +# 빌드에 필요한 도구 + 다운로드용 CA +RUN apt-get update \ + && apt-get install -y --no-install-recommends ca-certificates curl python3 \ + && rm -rf /var/lib/apt/lists/* + +WORKDIR /app + +# 디펜던시 캐시 분리 +COPY package.json package-lock.json* ./ +RUN npm install --no-audit --no-fund + +# 소스 복사 후 빌드 (yt-dlp 도 같이 받아둠) +COPY tsconfig.json ./ +COPY src ./src +COPY scripts ./scripts +COPY views ./views +COPY public ./public +RUN SKIP_NPM_INSTALL=1 node scripts/setup.mjs + +# ───── 런타임 스테이지 ────────────────────────────────────────────── +FROM node:22-bookworm-slim + +# ffmpeg 는 영상 trim 에 필요. yt-dlp 는 build 단계에서 받아둔 바이너리를 그대로 가져온다. +RUN apt-get update \ + && apt-get install -y --no-install-recommends ffmpeg ca-certificates \ + && rm -rf /var/lib/apt/lists/* + +WORKDIR /app +ENV NODE_ENV=production +# 컨테이너 안에서는 외부에서 들어오는 트래픽을 받아야 하니까 반드시 0.0.0.0. +ENV HOST=0.0.0.0 +ENV PORT=3000 + +# 빌드 산출물 + 런타임에 필요한 정적 자원만 복사 +COPY --from=build /app/node_modules ./node_modules +COPY --from=build /app/dist ./dist +COPY --from=build /app/bin ./bin +COPY --from=build /app/views ./views +COPY --from=build /app/public ./public +COPY --from=build /app/package.json ./package.json +COPY account.json ./account.json + +# 데이터 디렉토리 (영상/잡 영속화). docker-compose 에서 볼륨 마운트. +RUN mkdir -p /app/data/folders /app/data/jobs /app/data/tmp + +EXPOSE 3000 + +# tini 없이도 큰 영상 업로드/yt-dlp 자식 프로세스를 깔끔히 끄도록 신호를 받게 한다. +STOPSIGNAL SIGTERM + +CMD ["node", "dist/app.js"] diff --git a/README.md b/README.md index e00c210..e8055d1 100644 --- a/README.md +++ b/README.md @@ -6,8 +6,9 @@ 처음 한 번은 `setup` 으로 의존성 + yt-dlp 바이너리 + 빌드까지 한 번에 끝낼 수 있어요: ```bash -npm run setup # npm install + ./bin/yt-dlp 다운로드 + tsc -npm start # 기본 http://127.0.0.1:3000 (PORT=3000, HOST=127.0.0.1) +cp .env.example .env # 포트/호스트 등 환경변수 편집 +npm run setup # npm install + ./bin/yt-dlp 다운로드 + tsc +npm start # .env 의 HOST:PORT 로 바인딩 ``` 수동으로 단계별로 하고 싶다면: @@ -18,11 +19,29 @@ npm run build npm start ``` -- 외부 노출이 필요하면 `HOST=0.0.0.0 npm start` -- 관리자 비밀번호는 `account.json` 의 `password` 값 (초기값 `admin`, 운영 시 반드시 변경) -- 세션 비밀은 `SESSION_SECRET` 환경변수로 덮어쓰기 권장 -- 업로드 용량 한도: 기본 무제한. 제한하려면 `UPLOAD_MAX_BYTES=<바이트>` 설정 -- 대용량 업로드용 HTTP 요청 타임아웃: 기본 무제한(0). 필요시 `HTTP_REQUEST_TIMEOUT_MS=<밀리초>` +설정은 전부 `.env` 한 파일로 모았습니다 (`.env.example` 참고). 직접 실행과 Docker compose 양쪽에서 같은 파일을 씁니다. + +- `HOST` — 바인딩 주소. 외부 노출이 필요하면 `0.0.0.0`, 로컬 전용이면 `127.0.0.1`. Docker 안에서는 반드시 `0.0.0.0`. +- `PORT` — 바인딩 포트 (기본 `3000`). 바꾸면 compose 의 호스트 포트도 자동으로 따라갑니다. +- `SESSION_SECRET` — 운영 시 반드시 충분히 긴 랜덤 문자열로 교체. +- `UPLOAD_MAX_BYTES` — 업로드 용량 한도(바이트). 비우면 무제한. +- `HTTP_REQUEST_TIMEOUT_MS` — 대용량 업로드용 HTTP 요청 타임아웃(밀리초). `0`/미설정이면 무제한. +- 관리자 비밀번호는 `account.json` 의 `password` 값 (초기값 `admin`, 운영 시 반드시 변경). + +## Docker + +컨테이너로 띄울 거면 `Dockerfile` + `docker-compose.yml` 그대로 쓰면 됩니다. ffmpeg, yt-dlp 도 이미지에 포함됩니다. + +```bash +cp .env.example .env # 처음 한 번. HOST=0.0.0.0 인지 확인. +docker compose up -d --build +curl http://localhost:3000/ # PORT 바꿨으면 그 포트로 +# LAN 의 다른 기기에서: http://<호스트IP>:/ +``` + +핵심: 컨테이너 안에서는 반드시 `HOST=0.0.0.0` 이어야 외부 접근됩니다. compose 가 `.env` 의 `PORT` 를 읽어 호스트 포트 매핑 (`${PORT:-3000}:${PORT:-3000}`) 까지 자동으로 따라갑니다. + +데이터는 `./data` 가 `/app/data` 에 마운트되어 영속화됩니다 (`folders/`, `jobs/`, `tmp/`). ## 외부 의존 diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..88094ce --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,19 @@ +services: + app: + build: . + image: make-video-site:latest + container_name: make-video-site + restart: unless-stopped + ports: + # 호스트포트:컨테이너포트. ${PORT} 는 .env 에서 가져옵니다. + # 호스트 다른 포트로 매핑하고 싶으면 왼쪽만 바꾸세요 (예: "8080:${PORT:-3000}"). + - "${PORT:-3000}:${PORT:-3000}" + env_file: + # PORT/HOST/SESSION_SECRET/UPLOAD_MAX_BYTES/HTTP_REQUEST_TIMEOUT_MS 등을 + # 컨테이너 환경변수로 그대로 주입합니다. .env 파일이 없으면 만들고 시작하세요. + - .env + volumes: + # 영상/메타/잡 영속화. 호스트 디렉토리 경로는 환경에 맞게. + - ./data:/app/data + # 계정 파일을 컨테이너 외부에서 관리하고 싶다면 주석 해제 + # - ./account.json:/app/account.json:ro diff --git a/package-lock.json b/package-lock.json index 5e8f9f6..b816cef 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,7 @@ "name": "make-video-site", "version": "0.1.0", "dependencies": { + "dotenv": "^16.6.1", "ejs": "^3.1.10", "express": "^4.19.2", "express-session": "^1.18.0", @@ -342,6 +343,17 @@ "npm": "1.2.8000 || >= 1.4.16" } }, + "node_modules/dotenv": { + "version": "16.6.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz", + "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, "node_modules/dunder-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", diff --git a/package.json b/package.json index 8d571cb..a1ec311 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,7 @@ "dev": "tsc -p tsconfig.json && node dist/app.js" }, "dependencies": { + "dotenv": "^16.6.1", "ejs": "^3.1.10", "express": "^4.19.2", "express-session": "^1.18.0", diff --git a/src/app.ts b/src/app.ts index b9e0725..cfd6534 100644 --- a/src/app.ts +++ b/src/app.ts @@ -1,3 +1,4 @@ +import 'dotenv/config' import express from 'express' import session from 'express-session' import path from 'node:path'