# NOWEN - 高颜值 Bento 风格个人导航页 # 支持书签管理、分类管理、名言管理、自定义图标 # GitHub: https://github.com/cropflre/NOWEN # 支持多架构: linux/amd64, linux/arm64 (RK3588/RK3576/RK3566 等) # Build stage for frontend FROM node:20-alpine AS frontend-builder WORKDIR /app # 前端已移除 bcrypt 等 native 模块,无需编译工具链 COPY package*.json ./ RUN npm install COPY . . RUN npm run build # Production stage FROM node:20-alpine AS production # OCI 标准标签 LABEL org.opencontainers.image.title="NOWEN" LABEL org.opencontainers.image.description="高颜值 Bento 风格个人导航页,支持书签管理、分类管理、名言管理、自定义图标,一键 Docker 部署" LABEL org.opencontainers.image.url="https://github.com/cropflre/NOWEN" LABEL org.opencontainers.image.source="https://github.com/cropflre/NOWEN" LABEL org.opencontainers.image.vendor="cropflre" LABEL org.opencontainers.image.licenses="MIT" WORKDIR /app # 🔑 强制覆盖 apk 仓库源为官方源 # 绿联/飞牛等 NAS 系统可能向容器注入自定义仓库配置(如 openresty),导致依赖解析失败 RUN echo "https://dl-cdn.alpinelinux.org/alpine/v$(cut -d. -f1,2 /etc/alpine-release)/main" > /etc/apk/repositories && \ echo "https://dl-cdn.alpinelinux.org/alpine/v$(cut -d. -f1,2 /etc/alpine-release)/community" >> /etc/apk/repositories && \ apk update && \ apk add --no-cache ca-certificates nginx && \ # docker-cli 为可选依赖(仅用于系统监控 fallback),安装失败不阻断构建 (apk add --no-cache docker-cli || echo "⚠️ docker-cli not available, skipping (optional)") # Install production dependencies for backend # server 使用 bcryptjs(纯 JS)而非 bcrypt,无需编译工具 COPY server/package*.json ./server/ RUN cd server && npm install --omit=dev # Install tsx globally RUN npm install -g tsx # Copy backend source COPY server/src ./server/src COPY server/tsconfig.json ./server/ # Copy built frontend COPY --from=frontend-builder /app/dist ./dist # Copy nginx config COPY nginx.conf /etc/nginx/http.d/default.conf # Create data directory and safe backup directory # 🔑 安全备份使用 /app/.nowen-safe(不在 VOLUME 声明中,属于镜像层) # 这样即使 NAS Docker UI 重建容器,镜像层内的备份不受影响 RUN mkdir -p /app/server/data /app/.nowen-safe # ⚠️ 故意不声明 VOLUME! # 原因:绿联/飞牛等 NAS 的 Docker GUI 会为 VOLUME 声明的路径自动创建 # 以容器名为前缀的目录(如 cropflre_nowen-1/app_server_data), # 更新容器时容器名变化(变成 cropflre_nowen-2),新目录是空的,数据丢失。 # 不声明 VOLUME,让用户通过 docker-compose.yml 中的 volumes 手动挂载, # 或者 NAS GUI 的存储配置来指定固定路径。 # 🔑 防呆设计:直接在镜像内设置生产环境和固定 API 端口 # 用户通过 NAS GUI 部署时无需额外配置即可与 Nginx 反向代理保持一致 ENV NODE_ENV=production ENV PORT=39001 # 生产端口:Nginx Web 39000,Express API 39001 EXPOSE 39000 39001 # 🔑 确保容器停止时发送 SIGTERM,让 start.sh 有机会保存数据 STOPSIGNAL SIGTERM # Start script COPY start.sh /app/start.sh RUN sed -i 's/\r$//' /app/start.sh && \ sed -i 's/port 3001/port 39001/g; s/port 3000/port 39000/g' /app/start.sh && \ chmod +x /app/start.sh CMD ["/app/start.sh"]