From c82f887a02abd86ec40ac9503516bd76e3f1b97c Mon Sep 17 00:00:00 2001 From: yuanzonghao Date: Sat, 6 Jun 2026 21:57:57 +0800 Subject: [PATCH] feat(deploy): add Docker Compose self-hosted deployment option Add multi-platform Docker image build (amd64 + arm64) with GitHub Actions CI that pushes to GHCR on every merge to main. Users can self-host with a single `docker compose up -d` command. - Dockerfile: multi-stage build with Next.js standalone output (~150-200MB) - docker-compose.yml: one-command self-hosted deployment - .github/workflows/docker.yml: CI workflow with QEMU cross-compilation - next.config.ts: conditional `output: "standalone"` via BUILD_STANDALONE env - README (zh/en/ja): restructure deploy section to include Docker option Co-Authored-By: Claude Opus 4.6 --- .dockerignore | 26 ++++++++++++++++++ .github/workflows/docker.yml | 53 ++++++++++++++++++++++++++++++++++++ Dockerfile | 36 ++++++++++++++++++++++++ README.en.md | 26 ++++++++++++++++-- README.ja.md | 26 ++++++++++++++++-- README.md | 26 ++++++++++++++++-- docker-compose.yml | 8 ++++++ next.config.ts | 1 + 8 files changed, 196 insertions(+), 6 deletions(-) create mode 100644 .dockerignore create mode 100644 .github/workflows/docker.yml create mode 100644 Dockerfile create mode 100644 docker-compose.yml diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..605b16b --- /dev/null +++ b/.dockerignore @@ -0,0 +1,26 @@ +node_modules +.pnpm-store +.next +.open-next +.wrangler +.vercel +.turbo +.claude +.git + +dist +build +out + +.env +.env.local +.env.*.local +.dev.vars + +.DS_Store +*.log +npm-debug.log* +pnpm-debug.log* + +repomix-output.xml +users.md diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml new file mode 100644 index 0000000..19753d1 --- /dev/null +++ b/.github/workflows/docker.yml @@ -0,0 +1,53 @@ +name: Build and push Docker image + +on: + push: + branches: [main] + +env: + REGISTRY: ghcr.io + IMAGE_NAME: ${{ github.repository }} + +jobs: + build-and-push: + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Log in to GHCR + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Extract metadata + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + tags: | + type=raw,value=latest + type=sha,prefix=sha- + + - name: Build and push + uses: docker/build-push-action@v6 + with: + context: . + platforms: linux/amd64,linux/arm64 + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + cache-from: type=gha + cache-to: type=gha,mode=max diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..bc586b3 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,36 @@ +FROM node:22-alpine AS base +RUN corepack enable && corepack prepare pnpm@9.12.0 --activate + +# --- deps: install production + dev dependencies (cached layer) --- +FROM base AS deps +WORKDIR /app +COPY package.json pnpm-lock.yaml ./ +RUN pnpm install --frozen-lockfile + +# --- builder: build Next.js standalone output --- +FROM base AS builder +WORKDIR /app +COPY --from=deps /app/node_modules ./node_modules +COPY . . +ENV NEXT_TELEMETRY_DISABLED=1 +ENV BUILD_STANDALONE=true +RUN pnpm build + +# --- runner: minimal production image --- +FROM node:22-alpine AS runner +WORKDIR /app +ENV NODE_ENV=production +ENV NEXT_TELEMETRY_DISABLED=1 +ENV HOSTNAME=0.0.0.0 +ENV PORT=3000 + +RUN addgroup --system --gid 1001 nodejs && \ + adduser --system --uid 1001 nextjs + +COPY --from=builder --chown=nextjs:nodejs /app/public ./public +COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./ +COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static + +USER nextjs +EXPOSE 3000 +CMD ["node", "server.js"] diff --git a/README.en.md b/README.en.md index 93fac18..761ba52 100644 --- a/README.en.md +++ b/README.en.md @@ -37,14 +37,36 @@ Free to play, no setup required: [infiplot.com](https://infiplot.com) --- -## One-click deploy +## Deploy -InfiPlot deploys to both Vercel and Cloudflare Workers. Cloudflare deployment requires the Workers Paid Plan because the scene pipeline needs longer CPU time; for personal use, the one-click Vercel deploy is recommended. +InfiPlot offers multiple deployment options. For personal use, we recommend the one-click Vercel deploy; to self-host on your own server or local machine, use Docker. + +### Vercel / Cloudflare (one-click) + +Cloudflare deployment requires the Workers Paid Plan because the scene pipeline needs longer CPU time. [![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https://github.com/zonghaoyuan/infiplot&env=TEXT_BASE_URL,TEXT_API_KEY,TEXT_MODEL,IMAGE_BASE_URL,IMAGE_API_KEY,IMAGE_MODEL,VISION_BASE_URL,VISION_API_KEY,VISION_MODEL,TTS_BASE_URL,TTS_API_KEY,TTS_SPEECH_MODEL,MOCK_IMAGE&envDescription=Three%20required%20providers%20%2B%20optional%20TTS.%20Any%20OpenAI-compatible%20endpoint%20works%20for%20text%2Fvision.%20TTS%20uses%20MiMo%27s%20own%20protocol.&envLink=https://github.com/zonghaoyuan/infiplot/blob/main/README.en.md%23configuration-guide)   [![Deploy to Cloudflare](https://deploy.workers.cloudflare.com/button)](https://deploy.workers.cloudflare.com/?url=https://github.com/zonghaoyuan/infiplot) After deploy, fill in the environment variables — see the [Configuration guide](#configuration-guide) below. The repo root is the app itself: Vercel needs no special root directory; on Cloudflare, just set the build command to `pnpm build:cf`. +### Docker (self-hosted) + +For VPS, home servers, or local machines. Supports x86 and ARM (including Apple Silicon Macs). + +1. Copy `.env.example` to `.env.local` and fill in your API keys (see [Configuration guide](#configuration-guide)) +2. Start: + +```bash +docker compose up -d +``` + +Visit `http://localhost:3000` to start playing. + +> You can also run the image directly without Compose: +> ```bash +> docker run -d -p 3000:3000 --env-file .env.local ghcr.io/zonghaoyuan/infiplot:latest +> ``` + --- ## 📸 Screenshots diff --git a/README.ja.md b/README.ja.md index bb536ad..2a015af 100644 --- a/README.ja.md +++ b/README.ja.md @@ -37,14 +37,36 @@ InfiPlot は、AI がコンテンツをリアルタイムに生成するイン --- -## ワンクリックデプロイ +## デプロイ -InfiPlot は Vercel と Cloudflare Workers の両方にデプロイできます。Cloudflare へのデプロイはシーンパイプラインがより長い CPU 時間を必要とするため、Workers Paid Plan が必要です。個人利用には Vercel のワンクリックデプロイをおすすめします。 +InfiPlot は複数のデプロイ方法に対応しています。個人利用には Vercel のワンクリックデプロイをおすすめします。自分のサーバーやローカルマシンで動かしたい場合は Docker を使ってください。 + +### Vercel / Cloudflare(ワンクリック) + +Cloudflare へのデプロイはシーンパイプラインがより長い CPU 時間を必要とするため、Workers Paid Plan が必要です。 [![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https://github.com/zonghaoyuan/infiplot&env=TEXT_BASE_URL,TEXT_API_KEY,TEXT_MODEL,IMAGE_BASE_URL,IMAGE_API_KEY,IMAGE_MODEL,VISION_BASE_URL,VISION_API_KEY,VISION_MODEL,TTS_BASE_URL,TTS_API_KEY,TTS_SPEECH_MODEL,MOCK_IMAGE&envDescription=Three%20required%20providers%20%2B%20optional%20TTS.%20Any%20OpenAI-compatible%20endpoint%20works%20for%20text%2Fvision.%20TTS%20uses%20MiMo%27s%20own%20protocol.&envLink=https://github.com/zonghaoyuan/infiplot/blob/main/README.ja.md%23%E8%A8%AD%E5%AE%9A%E3%82%AC%E3%82%A4%E3%83%89)   [![Deploy to Cloudflare](https://deploy.workers.cloudflare.com/button)](https://deploy.workers.cloudflare.com/?url=https://github.com/zonghaoyuan/infiplot) デプロイ後、環境変数を設定してください —— 下記の[設定ガイド](#設定ガイド)を参照。リポジトリのルートがアプリ本体です:Vercel では特別なルート設定は不要です。Cloudflare ではビルドコマンドを `pnpm build:cf` に設定するだけで済みます。 +### Docker デプロイ(セルフホスト) + +VPS、ホームサーバー、ローカルマシンに対応。x86 と ARM(Apple Silicon Mac を含む)をサポート。 + +1. `.env.example` を `.env.local` にコピーし、API キーを設定([設定ガイド](#設定ガイド)を参照) +2. 起動: + +```bash +docker compose up -d +``` + +`http://localhost:3000` にアクセスしてゲームを開始できます。 + +> Compose を使わず、直接イメージを実行することもできます: +> ```bash +> docker run -d -p 3000:3000 --env-file .env.local ghcr.io/zonghaoyuan/infiplot:latest +> ``` + --- ## 📸 スクリーンショット diff --git a/README.md b/README.md index edafdea..a432089 100644 --- a/README.md +++ b/README.md @@ -37,14 +37,36 @@ InfiPlot是一款AI实时生成内容的互动剧情游戏,这里没有预设 --- -## 一键部署 +## 部署 -InfiPlot 同时支持部署到 Vercel 与 Cloudflare Workers。Cloudflare 部署因场景流水线需要更长 CPU 时间,需要 Workers Paid Plan;个人使用推荐用 Vercel 一键部署。 +InfiPlot 支持多种部署方式。个人使用推荐 Vercel 一键部署;想部署到自己的服务器或本地运行,可以用 Docker。 + +### Vercel / Cloudflare(一键部署) + +Cloudflare 部署因场景流水线需要更长 CPU 时间,需要 Workers Paid Plan。 [![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https://github.com/zonghaoyuan/infiplot&env=TEXT_BASE_URL,TEXT_API_KEY,TEXT_MODEL,IMAGE_BASE_URL,IMAGE_API_KEY,IMAGE_MODEL,VISION_BASE_URL,VISION_API_KEY,VISION_MODEL,TTS_BASE_URL,TTS_API_KEY,TTS_SPEECH_MODEL,MOCK_IMAGE&envDescription=Three%20required%20providers%20%2B%20optional%20TTS.%20Any%20OpenAI-compatible%20endpoint%20works%20for%20text%2Fvision.%20TTS%20uses%20MiMo%27s%20own%20protocol.&envLink=https://github.com/zonghaoyuan/infiplot%23%E9%85%8D%E7%BD%AE%E6%95%99%E7%A8%8B)   [![Deploy to Cloudflare](https://deploy.workers.cloudflare.com/button)](https://deploy.workers.cloudflare.com/?url=https://github.com/zonghaoyuan/infiplot) 部署完成后,填好环境变量 —— 详见下方的[配置教程](#配置教程)。仓库根目录就是应用本身:Vercel 无需额外设置 root directory;在 Cloudflare 上把构建命令设为 `pnpm build:cf` 即可。 +### Docker 部署(自托管) + +适用于 VPS、家庭服务器或本地电脑。支持 x86 和 ARM(含 Apple Silicon Mac)。 + +1. 复制 `.env.example` 为 `.env.local`,填入你的 API Key(详见[配置教程](#配置教程)) +2. 启动: + +```bash +docker compose up -d +``` + +访问 `http://localhost:3000` 即可开始游戏。 + +> 也可以不用 Compose,直接运行镜像: +> ```bash +> docker run -d -p 3000:3000 --env-file .env.local ghcr.io/zonghaoyuan/infiplot:latest +> ``` + --- ## 📸 游戏截图 diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..455027f --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,8 @@ +services: + infiplot: + image: ghcr.io/zonghaoyuan/infiplot:latest + ports: + - "3000:3000" + env_file: + - .env.local + restart: unless-stopped diff --git a/next.config.ts b/next.config.ts index 36a22a4..9fb7dd7 100644 --- a/next.config.ts +++ b/next.config.ts @@ -1,6 +1,7 @@ import type { NextConfig } from "next"; const config: NextConfig = { + output: process.env.BUILD_STANDALONE === "true" ? "standalone" : undefined, reactStrictMode: true, typedRoutes: false, turbopack: {