diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..957971b --- /dev/null +++ b/.dockerignore @@ -0,0 +1,56 @@ +# Dependencies +node_modules +.pnp +.pnp.js + +# Build outputs +.next +out +build +dist + +# Development files +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +.pnpm-debug.log* + +# IDE +.idea +.vscode +*.swp +*.swo + +# OS files +.DS_Store +Thumbs.db + +# Environment files +.env +.env.local +.env.development.local +.env.test.local +.env.production.local + +# Git +.git +.gitignore + +# Docker +Dockerfile +docker-compose*.yml +.dockerignore + +# Tests +coverage +.nyc_output +*.test.ts +*.spec.ts +__tests__ + +# Misc +*.md +!README.md +.eslintcache +.tsbuildinfo diff --git a/.gitea/workflows/docker-build.yaml b/.gitea/workflows/docker-build.yaml new file mode 100644 index 0000000..98d46f8 --- /dev/null +++ b/.gitea/workflows/docker-build.yaml @@ -0,0 +1,68 @@ +name: Build and Publish Mana Loop Docker Image + +on: + push: + branches: + - master + - main + workflow_dispatch: + inputs: + image_tag: + description: "Custom image tag (optional)" + required: false + default: "" + +jobs: + build-and-publish: + runs-on: ubuntu-latest + env: + IMAGE_HOST: gitea.tailf367e3.ts.net + IMAGE_OWNER: anexim + IMAGE_NAME: mana-loop + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Set up Docker BuildX + uses: docker/setup-buildx-action@v3 + + - name: Login to Gitea Container Registry + uses: docker/login-action@v3 + with: + registry: ${{ env.IMAGE_HOST }} + username: ${{ secrets.REGISTRY_USERNAME }} + password: ${{ secrets.REGISTRY_PASSWORD }} + + - name: Extract metadata for Docker + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ env.IMAGE_HOST }}/${{ env.IMAGE_OWNER }}/${{ env.IMAGE_NAME }} + tags: | + type=ref,event=branch + type=sha,prefix= + type=raw,value=latest,enable={{is_default_branch}} + + - name: Build and push Docker image + uses: docker/build-push-action@v6 + with: + context: . + push: true + tags: | + ${{ env.IMAGE_HOST }}/${{ env.IMAGE_OWNER }}/${{ env.IMAGE_NAME }}:latest + ${{ github.event.inputs.image_tag != '' && format('{0}/{1}/{2}:{3}', env.IMAGE_HOST, env.IMAGE_OWNER, env.IMAGE_NAME, github.event.inputs.image_tag) || '' }} + ${{ env.IMAGE_HOST }}/${{ env.IMAGE_OWNER }}/${{ env.IMAGE_NAME }}:${{ github.sha }} + platforms: linux/amd64 + file: Dockerfile + cache-from: type=gha + cache-to: type=gha,mode=max + + - name: Image digest + run: | + echo "Successfully pushed image tags:" + echo " - ${{ env.IMAGE_HOST }}/${{ env.IMAGE_OWNER }}/${{ env.IMAGE_NAME }}:latest" + echo " - ${{ env.IMAGE_HOST }}/${{ env.IMAGE_OWNER }}/${{ env.IMAGE_NAME }}:${{ github.sha }}" + if [ -n "${{ github.event.inputs.image_tag }}" ]; then + echo " - ${{ env.IMAGE_HOST }}/${{ env.IMAGE_OWNER }}/${{ env.IMAGE_NAME }}:${{ github.event.inputs.image_tag }}" + fi diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..a4813af --- /dev/null +++ b/Dockerfile @@ -0,0 +1,78 @@ +# Mana Loop - Next.js Game Docker Image +# Multi-stage build for optimized production image + +# Stage 1: Dependencies +FROM node:20-alpine AS deps +WORKDIR /app + +# Install dependencies for native modules +RUN apk add --no-cache libc6-compat + +# Copy package files +COPY package.json bun.lockb* ./ +COPY prisma ./prisma/ + +# Install bun for faster installs +RUN npm install -g bun + +# Install dependencies +RUN bun install --frozen-lockfile --production=false + +# Stage 2: Builder +FROM node:20-alpine AS builder +WORKDIR /app + +# Copy dependencies from deps stage +COPY --from=deps /app/node_modules ./node_modules +COPY . . + +# Install bun for build +RUN npm install -g bun + +# Set environment variables for build +ENV NEXT_TELEMETRY_DISABLED=1 +ENV NODE_ENV=production + +# Generate Prisma client +RUN bun run db:generate + +# Build the application +RUN bun run build + +# Stage 3: Runner +FROM node:20-alpine AS runner +WORKDIR /app + +ENV NODE_ENV=production +ENV NEXT_TELEMETRY_DISABLED=1 + +# Create non-root user for security +RUN addgroup --system --gid 1001 nodejs +RUN adduser --system --uid 1001 nextjs + +# Copy necessary files from builder +COPY --from=builder /app/public ./public +COPY --from=builder /app/.next/standalone ./ +COPY --from=builder /app/.next/static ./.next/static +COPY --from=builder /app/prisma ./prisma +COPY --from=builder /app/node_modules/.prisma ./node_modules/.prisma +COPY --from=builder /app/node_modules/@prisma ./node_modules/@prisma + +# Set correct ownership +RUN chown -R nextjs:nodejs /app + +# Switch to non-root user +USER nextjs + +# Expose port +EXPOSE 3000 + +ENV PORT=3000 +ENV HOSTNAME="0.0.0.0" + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \ + CMD wget --no-verbose --tries=1 --spider http://localhost:3000 || exit 1 + +# Start the server +CMD ["node", "server.js"] diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..d9271ca --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,15 @@ +services: + mana-loop: + image: gitea.tailf367e3.ts.net/anexim/mana-loop:latest + container_name: mana-loop + restart: unless-stopped + ports: + - "3000:3000" + environment: + - NODE_ENV=production + healthcheck: + test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:3000"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 10s