diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..1d61047 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,61 @@ +# Dependencies +node_modules +.npm + +# Next.js build output +.next +out +build + +# Git +.git +.gitignore + +# Environment files +.env +.env.local +.env.*.local + +# IDE +.idea +.vscode +*.swp +*.swo + +# OS +.DS_Store +Thumbs.db + +# Logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# Test +coverage + +# Docker +docker-compose*.yml +Dockerfile* +.dockerignore + +# Data files (keep structure but ignore content) +data/* + +# env.docker (template, not actual env) +.env.docker + +# Misc +README.md +AGENTS.md +*.md + +# TypeScript +*.tsbuildinfo +next-env.d.ts + +# Debug +npm +pnpm-lock.yaml +yarn.lock diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..11cdb94 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,43 @@ +# Dockerfile for Recipe Translation Tool +# Stage 1: Builder +FROM node:20-alpine AS builder + +WORKDIR /app + +# Copy package files +COPY package.json pnpm-lock.yaml ./ + +# Install dependencies +RUN corepack enable && pnpm install --frozen-lockfile + +# Copy source code +COPY . . + +# Build the application +RUN pnpm build + +# Stage 2: Runner +FROM node:20-alpine AS runner + +WORKDIR /app + +ENV NODE_ENV=production + +# Create non-root user +RUN addgroup --system --gid 1001 nodejs && \ + adduser --system --uid 1001 nextjs + +# Copy built application +COPY --from=builder /app/public ./public +COPY --from=builder /app/.next/standalone ./ +COPY --from=builder /app/.next/static ./.next/static + +# Set ownership +USER nextjs + +EXPOSE 3000 + +ENV PORT=3000 +ENV HOSTNAME="0.0.0.0" + +CMD ["node", "server.js"] diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..2f77240 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,52 @@ +services: + app: + build: + context: . + dockerfile: Dockerfile + container_name: recipe_tool_app + restart: unless-stopped + ports: + - "3000:3000" + environment: + - NODE_ENV=production + - DB_HOST=db + - DB_PORT=3306 + - DB_USER=${DB_USER:-root} + - DB_PASSWORD=${DB_PASSWORD:-123456} + - DB_NAME=${DB_NAME:-recipe_tools} + - AI_API_KEY=${AI_API_KEY} + - AI_BASE_URL=${AI_BASE_URL:-https://api.openai.com/v1} + - AI_MODEL=${AI_MODEL:-gpt-3.5-turbo} + depends_on: + db: + condition: service_healthy + networks: + - recipe_network + + db: + image: mysql:8.0 + container_name: recipe_tool_db + restart: unless-stopped + environment: + - MYSQL_ROOT_PASSWORD=${DB_PASSWORD:-123456} + - MYSQL_DATABASE=${DB_NAME:-recipe_tools} + ports: + - "3306:3306" + volumes: + - mysql_data:/var/lib/mysql + # 初始化脚本(可选) + # - ./docker/mysql/init.sql:/docker-entrypoint-initdb.d/init.sql + healthcheck: + test: ["CMD", "mysqladmin", "ping", "-h", "localhost"] + interval: 10s + timeout: 5s + retries: 5 + networks: + - recipe_network + +networks: + recipe_network: + driver: bridge + +volumes: + mysql_data: diff --git a/next.config.ts b/next.config.ts index e9ffa30..68a6c64 100644 --- a/next.config.ts +++ b/next.config.ts @@ -1,7 +1,7 @@ import type { NextConfig } from "next"; const nextConfig: NextConfig = { - /* config options here */ + output: "standalone", }; export default nextConfig;