- 文件上传验证: ModelUploader组件增强、上传API错误处理 - GLTF解析器: 增强节点提取和元数据 - 板材编辑: PanelEditor客户端验证 + panels API服务端验证 - 装配步骤编辑: 修复未使用代码,添加预览按钮 - 装配动画播放: AssemblyPlayer步骤导航边界检查优化 - 分享链接: 完整CRUD API(创建、获取、删除),含验证和过期检查 跳过可选测试任务以加快MVP开发
240 lines
9.0 KiB
Docker
240 lines
9.0 KiB
Docker
# ============================================
|
||
# 3D家具安装展示系统 - Docker构建文件
|
||
# ============================================
|
||
# 基于 flnxi_sucai 项目的 Dockerfile 优化
|
||
# 支持本地缓存包,减少网络依赖
|
||
|
||
# ============================================
|
||
# 阶段 1: 基础镜像
|
||
# ============================================
|
||
FROM node:18-alpine AS base
|
||
|
||
# 使用阿里云镜像源加速(中国网络优化)
|
||
RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories
|
||
|
||
# ============================================
|
||
# 阶段 2: 依赖安装 (deps)
|
||
# ============================================
|
||
FROM base AS deps
|
||
|
||
# 安装构建时依赖
|
||
# Three.js 和 3D 相关包需要的系统依赖
|
||
RUN apk add --no-cache --virtual .build-deps \
|
||
libc6-compat \
|
||
openssl3 \
|
||
openssl-dev \
|
||
# 3D 图形处理相关依赖
|
||
mesa-dev \
|
||
mesa-gl \
|
||
# 编译工具(用于编译原生模块)
|
||
python3 \
|
||
make \
|
||
g++ \
|
||
pkgconfig
|
||
|
||
WORKDIR /app
|
||
|
||
# 复制依赖文件(利用 Docker 缓存层)
|
||
COPY package.json package-lock.json* ./
|
||
|
||
# 配置 npm 使用国内镜像源
|
||
RUN npm config set registry https://registry.npmmirror.com/
|
||
|
||
# 尝试使用本地缓存的 node_modules
|
||
# 如果本地缓存存在,直接复制;否则通过 npm 安装
|
||
COPY local-packages/npm-cache/node_modules* ./node_modules_cache/
|
||
RUN if [ -d "./node_modules_cache" ] && [ "$(ls -A ./node_modules_cache)" ]; then \
|
||
echo "使用本地缓存的 node_modules" && \
|
||
cp -r ./node_modules_cache/* ./node_modules/ 2>/dev/null || true && \
|
||
npm ci --prefer-offline --no-audit || npm install --prefer-offline --no-audit; \
|
||
else \
|
||
echo "本地缓存不可用,从网络安装依赖" && \
|
||
npm ci --prefer-offline --no-audit || npm install --prefer-offline --no-audit; \
|
||
fi
|
||
|
||
# 清理构建依赖,保留运行时库
|
||
RUN apk del .build-deps && \
|
||
npm config delete registry && \
|
||
apk add --no-cache \
|
||
libc6-compat \
|
||
ca-certificates \
|
||
openssl3 \
|
||
# 3D 渲染运行时库
|
||
mesa-gl
|
||
|
||
# ============================================
|
||
# 阶段 3: 构建应用 (builder)
|
||
# ============================================
|
||
FROM base AS builder
|
||
|
||
# 构建参数
|
||
ARG DATABASE_URL="postgresql://user:password@localhost:5432/placeholder_db?schema=public"
|
||
ARG NEXTAUTH_URL=""
|
||
ARG NEXT_PUBLIC_APP_URL=""
|
||
|
||
# Prisma 引擎缓存配置
|
||
ARG PRISMA_ENGINE_CACHE_FILE_NAME="libquery_engine-linux-musl-openssl-3.0.x.so.node"
|
||
ARG PRISMA_SCHEMA_ENGINE_CACHE_FILE_NAME="schema-engine-linux-musl-openssl-3.0.x"
|
||
|
||
# 环境变量设置
|
||
ENV PRISMA_ENGINE_CACHE_FILE_NAME=${PRISMA_ENGINE_CACHE_FILE_NAME}
|
||
ENV PRISMA_SCHEMA_ENGINE_CACHE_FILE_NAME=${PRISMA_SCHEMA_ENGINE_CACHE_FILE_NAME}
|
||
ENV DATABASE_URL=${DATABASE_URL}
|
||
ENV NEXTAUTH_URL=${NEXTAUTH_URL}
|
||
ENV NEXT_PUBLIC_APP_URL=${NEXT_PUBLIC_APP_URL}
|
||
ENV NEXT_TELEMETRY_DISABLED=1
|
||
ENV NODE_ENV=production
|
||
ENV PRISMA_ENGINES_CHECKSUM_IGNORE_MISSING=1
|
||
|
||
WORKDIR /app
|
||
|
||
# 安装 OpenSSL 3(Prisma 需要)
|
||
RUN apk add --no-cache openssl3
|
||
|
||
# 从 deps 阶段复制 node_modules
|
||
COPY --from=deps /app/node_modules ./node_modules
|
||
|
||
# 复制应用源代码
|
||
COPY . .
|
||
|
||
# 配置 Prisma 引擎缓存路径
|
||
ENV PRISMA_ENGINE_CACHE_PATH=/app/local-packages/prisma-engines/${PRISMA_ENGINE_CACHE_FILE_NAME}
|
||
ENV PRISMA_SCHEMA_ENGINE_CACHE_PATH=/app/local-packages/prisma-engines/${PRISMA_SCHEMA_ENGINE_CACHE_FILE_NAME}
|
||
|
||
# 设置 Prisma 引擎缓存(如果本地缓存存在)
|
||
RUN set -ex && \
|
||
if [ -f "${PRISMA_ENGINE_CACHE_PATH}" ]; then \
|
||
echo "检测到本地 Prisma 查询引擎缓存: ${PRISMA_ENGINE_CACHE_PATH}" && \
|
||
PRISMA_ENGINE_VERSION=$(node -p "require('@prisma/engines-version/package.json').prisma.enginesVersion") && \
|
||
TARGET_DIR="/root/.cache/prisma/master/${PRISMA_ENGINE_VERSION}/linux-musl-openssl-3.0.x" && \
|
||
mkdir -p "${TARGET_DIR}" && \
|
||
cp "${PRISMA_ENGINE_CACHE_PATH}" "${TARGET_DIR}/libquery_engine.so.node" && \
|
||
gzip -c "${PRISMA_ENGINE_CACHE_PATH}" > "${TARGET_DIR}/libquery_engine.so.node.gz" && \
|
||
sha256sum "${TARGET_DIR}/libquery_engine.so.node.gz" | awk '{print $1}' > "${TARGET_DIR}/libquery_engine.so.node.gz.sha256"; \
|
||
else \
|
||
echo "未找到本地 Prisma 查询引擎缓存,将在线下载"; \
|
||
fi && \
|
||
if [ -f "${PRISMA_SCHEMA_ENGINE_CACHE_PATH}" ]; then \
|
||
echo "检测到本地 Prisma schema 引擎缓存: ${PRISMA_SCHEMA_ENGINE_CACHE_PATH}" && \
|
||
PRISMA_ENGINE_VERSION=$(node -p "require('@prisma/engines-version/package.json').prisma.enginesVersion") && \
|
||
TARGET_DIR="/root/.cache/prisma/master/${PRISMA_ENGINE_VERSION}/linux-musl-openssl-3.0.x" && \
|
||
mkdir -p "${TARGET_DIR}" && \
|
||
cp "${PRISMA_SCHEMA_ENGINE_CACHE_PATH}" "${TARGET_DIR}/schema-engine" && \
|
||
chmod +x "${TARGET_DIR}/schema-engine" && \
|
||
gzip -c "${PRISMA_SCHEMA_ENGINE_CACHE_PATH}" > "${TARGET_DIR}/schema-engine.gz" && \
|
||
sha256sum "${TARGET_DIR}/schema-engine.gz" | awk '{print $1}' > "${TARGET_DIR}/schema-engine.gz.sha256"; \
|
||
else \
|
||
echo "未找到本地 Prisma schema 引擎缓存,将在线下载"; \
|
||
fi
|
||
|
||
# 生成 Prisma Client(构建阶段使用占位 DATABASE_URL,避免 prisma.config.ts 报错)
|
||
RUN set -ex && \
|
||
export DATABASE_URL="${DATABASE_URL:-postgresql://user:password@localhost:5432/placeholder_db?schema=public}" && \
|
||
if [ -f "${PRISMA_ENGINE_CACHE_PATH}" ]; then \
|
||
export PRISMA_QUERY_ENGINE_LIBRARY="${PRISMA_ENGINE_CACHE_PATH}" && \
|
||
export PRISMA_CLIENT_ENGINE_TYPE=library && \
|
||
export PRISMA_CLI_QUERY_ENGINE_TYPE=library && \
|
||
echo "使用本地 Prisma 查询引擎缓存生成 Client"; \
|
||
else \
|
||
echo "使用在线下载的 Prisma 引擎"; \
|
||
unset PRISMA_QUERY_ENGINE_LIBRARY PRISMA_CLIENT_ENGINE_TYPE PRISMA_CLI_QUERY_ENGINE_TYPE; \
|
||
fi && \
|
||
if [ -f "${PRISMA_SCHEMA_ENGINE_CACHE_PATH}" ]; then \
|
||
export PRISMA_SCHEMA_ENGINE_BINARY="${PRISMA_SCHEMA_ENGINE_CACHE_PATH}" && \
|
||
echo "使用本地 Prisma schema 引擎缓存"; \
|
||
else \
|
||
unset PRISMA_SCHEMA_ENGINE_BINARY; \
|
||
fi && \
|
||
if npx prisma generate; then \
|
||
echo "Prisma Client 生成成功"; \
|
||
else \
|
||
echo "Prisma 官方源失败,尝试使用镜像源..." && \
|
||
PRISMA_ENGINES_MIRROR="https://registry.npmmirror.com/-/binary/prisma/" npx prisma generate; \
|
||
fi
|
||
|
||
# 验证 Prisma Client 生成
|
||
RUN if [ ! -d "node_modules/.prisma" ]; then \
|
||
echo "错误: Prisma Client 未生成" >&2 && exit 1; \
|
||
fi
|
||
|
||
# 构建 Next.js 应用(standalone 模式)
|
||
RUN set -ex && \
|
||
if [ -f "${PRISMA_ENGINE_CACHE_PATH}" ]; then \
|
||
export PRISMA_QUERY_ENGINE_LIBRARY="${PRISMA_ENGINE_CACHE_PATH}" && \
|
||
export PRISMA_CLIENT_ENGINE_TYPE=library && \
|
||
export PRISMA_CLI_QUERY_ENGINE_TYPE=library; \
|
||
else \
|
||
unset PRISMA_QUERY_ENGINE_LIBRARY PRISMA_CLIENT_ENGINE_TYPE PRISMA_CLI_QUERY_ENGINE_TYPE; \
|
||
fi && \
|
||
if [ -f "${PRISMA_SCHEMA_ENGINE_CACHE_PATH}" ]; then \
|
||
export PRISMA_SCHEMA_ENGINE_BINARY="${PRISMA_SCHEMA_ENGINE_CACHE_PATH}"; \
|
||
else \
|
||
unset PRISMA_SCHEMA_ENGINE_BINARY; \
|
||
fi && \
|
||
npm run build
|
||
|
||
# 验证构建产物
|
||
RUN if [ ! -f ".next/standalone/server.js" ]; then \
|
||
echo "错误: standalone 输出未生成,请检查 next.config.js" >&2 && \
|
||
exit 1; \
|
||
fi
|
||
|
||
# ============================================
|
||
# 阶段 4: 生产运行 (runner)
|
||
# ============================================
|
||
FROM base AS runner
|
||
|
||
WORKDIR /app
|
||
|
||
# 设置生产环境变量
|
||
ENV NODE_ENV=production
|
||
ENV NEXT_TELEMETRY_DISABLED=1
|
||
ENV PORT=3000
|
||
ENV HOSTNAME="0.0.0.0"
|
||
|
||
# 安装运行时依赖
|
||
RUN apk add --no-cache \
|
||
libc6-compat \
|
||
ca-certificates \
|
||
openssl3 \
|
||
# 3D 渲染运行时库
|
||
mesa-gl \
|
||
# 字体支持(3D 文本渲染)
|
||
fontconfig \
|
||
ttf-dejavu
|
||
|
||
# 创建非 root 用户
|
||
RUN addgroup --system --gid 1001 nodejs && \
|
||
adduser --system --uid 1001 nextjs
|
||
|
||
# 从 builder 阶段复制构建产物
|
||
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
|
||
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
|
||
|
||
# 复制必要的文件
|
||
COPY --from=builder --chown=nextjs:nodejs /app/public ./public
|
||
COPY --from=builder --chown=nextjs:nodejs /app/prisma ./prisma
|
||
COPY --from=builder --chown=nextjs:nodejs /app/src/lib ./src/lib
|
||
|
||
# 复制 Prisma 相关文件
|
||
COPY --from=builder --chown=nextjs:nodejs /app/node_modules/.prisma ./node_modules/.prisma
|
||
COPY --from=builder --chown=nextjs:nodejs /app/node_modules/@prisma ./node_modules/@prisma
|
||
COPY --from=builder --chown=nextjs:nodejs /app/node_modules/prisma ./node_modules/prisma
|
||
COPY --from=builder --chown=nextjs:nodejs /app/node_modules/.bin ./node_modules/.bin
|
||
|
||
# 创建上传目录
|
||
RUN mkdir -p /app/uploads && \
|
||
chown -R nextjs:nodejs /app/uploads && \
|
||
chmod -R 755 /app/uploads
|
||
|
||
# 切换到非 root 用户
|
||
USER nextjs
|
||
|
||
EXPOSE 3000
|
||
|
||
# 健康检查
|
||
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
|
||
CMD curl -f http://localhost:3000/api/health || exit 1
|
||
|
||
# 启动应用
|
||
CMD ["node", "server.js"] |