From 4077661a7058a46f019e5f44e7bf0ef9cbbc906e Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sun, 2 Jun 2024 19:58:53 -0600 Subject: [PATCH] feat: add project --- .dockerignore | 0 .env | 5 + .gitignore | 36 +++ docker-compose.dev.yml | 33 +++ docker-compose.prod-without-multistage.yml | 24 ++ docker-compose.prod.yml | 24 ++ next-app/.gitignore | 35 +++ next-app/dev.Dockerfile | 32 ++ next-app/next.config.js | 6 + next-app/package.json | 19 ++ next-app/pnpm-lock.yaml | 310 ++++++++++++++++++++ next-app/prod-without-multistage.Dockerfile | 48 +++ next-app/prod.Dockerfile | 73 +++++ next-app/public/favicon.ico | Bin 0 -> 15086 bytes next-app/public/vercel.svg | 4 + next-app/src/pages/_app.tsx | 6 + next-app/src/pages/index.tsx | 70 +++++ next-app/src/styles/Home.module.css | 137 +++++++++ next-app/src/styles/globals.css | 26 ++ next-app/tsconfig.json | 20 ++ readme.md | 101 +++++++ 21 files changed, 1009 insertions(+) create mode 100644 .dockerignore create mode 100644 .env create mode 100644 .gitignore create mode 100644 docker-compose.dev.yml create mode 100644 docker-compose.prod-without-multistage.yml create mode 100644 docker-compose.prod.yml create mode 100644 next-app/.gitignore create mode 100644 next-app/dev.Dockerfile create mode 100644 next-app/next.config.js create mode 100644 next-app/package.json create mode 100644 next-app/pnpm-lock.yaml create mode 100644 next-app/prod-without-multistage.Dockerfile create mode 100644 next-app/prod.Dockerfile create mode 100644 next-app/public/favicon.ico create mode 100644 next-app/public/vercel.svg create mode 100644 next-app/src/pages/_app.tsx create mode 100644 next-app/src/pages/index.tsx create mode 100644 next-app/src/styles/Home.module.css create mode 100644 next-app/src/styles/globals.css create mode 100644 next-app/tsconfig.json diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..e69de29 diff --git a/.env b/.env new file mode 100644 index 0000000..30083c1 --- /dev/null +++ b/.env @@ -0,0 +1,5 @@ +# DO NOT ADD SECRETS TO THIS FILE. This is a good place for defaults. +# If you want to add secrets use `.env.production.local` instead, which is automatically detected by `docker compose`. + +ENV_VARIABLE=production_server_only_variable +NEXT_PUBLIC_ENV_VARIABLE=production_public_variable diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fd3dbb5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,36 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.js +.yarn/install-state.gz + +# testing +/coverage + +# next.js +/.next/ +/out/ + +# production +/build + +# misc +.DS_Store +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# local env files +.env*.local + +# vercel +.vercel + +# typescript +*.tsbuildinfo +next-env.d.ts diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml new file mode 100644 index 0000000..747e9c1 --- /dev/null +++ b/docker-compose.dev.yml @@ -0,0 +1,33 @@ +version: "3" + +services: + next-app: + container_name: next-app + build: + context: ./next-app + dockerfile: dev.Dockerfile + + # Set environment variables directly in the docker-compose file + environment: + ENV_VARIABLE: ${ENV_VARIABLE} + NEXT_PUBLIC_ENV_VARIABLE: ${NEXT_PUBLIC_ENV_VARIABLE} + + # Set environment variables based on the .env file + env_file: + - .env + volumes: + - ./next-app/src:/app/src + - ./next-app/public:/app/public + restart: always + ports: + - 3000:3000 + networks: + - my_network + + # Add more containers below (nginx, postgres, etc.) + +# Define a network, which allows containers to communicate +# with each other, by using their container name as a hostname +networks: + my_network: + external: true diff --git a/docker-compose.prod-without-multistage.yml b/docker-compose.prod-without-multistage.yml new file mode 100644 index 0000000..908347c --- /dev/null +++ b/docker-compose.prod-without-multistage.yml @@ -0,0 +1,24 @@ +version: "3" + +services: + next-app: + container_name: next-app + build: + context: ./next-app + dockerfile: prod-without-multistage.Dockerfile + args: + ENV_VARIABLE: ${ENV_VARIABLE} + NEXT_PUBLIC_ENV_VARIABLE: ${NEXT_PUBLIC_ENV_VARIABLE} + restart: always + ports: + - 3000:3000 + networks: + - my_network + + # Add more containers below (nginx, postgres, etc.) + +# Define a network, which allows containers to communicate +# with each other, by using their container name as a hostname +networks: + my_network: + external: true diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml new file mode 100644 index 0000000..01b7e9b --- /dev/null +++ b/docker-compose.prod.yml @@ -0,0 +1,24 @@ +version: "3" + +services: + next-app: + container_name: next-app + build: + context: ./next-app + dockerfile: prod.Dockerfile + args: + ENV_VARIABLE: ${ENV_VARIABLE} + NEXT_PUBLIC_ENV_VARIABLE: ${NEXT_PUBLIC_ENV_VARIABLE} + restart: always + ports: + - 3000:3000 + networks: + - my_network + + # Add more containers below (nginx, postgres, etc.) + +# Define a network, which allows containers to communicate +# with each other, by using their container name as a hostname +networks: + my_network: + external: true diff --git a/next-app/.gitignore b/next-app/.gitignore new file mode 100644 index 0000000..083967f --- /dev/null +++ b/next-app/.gitignore @@ -0,0 +1,35 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.js +.yarn/install-state.gz + +# testing +/coverage + +# next.js +/.next/ +/out/ + +# production +/build + +# misc +.DS_Store +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# local env files +.env.local +.env.development.local +.env.test.local +.env.production.local + +# vercel +.vercel diff --git a/next-app/dev.Dockerfile b/next-app/dev.Dockerfile new file mode 100644 index 0000000..d884b71 --- /dev/null +++ b/next-app/dev.Dockerfile @@ -0,0 +1,32 @@ +FROM node:18-alpine + +WORKDIR /app + +# Install dependencies based on the preferred package manager +COPY package.json yarn.lock* package-lock.json* pnpm-lock.yaml* ./ +RUN \ + if [ -f yarn.lock ]; then yarn --frozen-lockfile; \ + elif [ -f package-lock.json ]; then npm ci; \ + elif [ -f pnpm-lock.yaml ]; then corepack enable pnpm && pnpm i; \ + # Allow install without lockfile, so example works even without Node.js installed locally + else echo "Warning: Lockfile not found. It is recommended to commit lockfiles to version control." && yarn install; \ + fi + +COPY src ./src +COPY public ./public +COPY next.config.js . +COPY tsconfig.json . + +# Next.js collects completely anonymous telemetry data about general usage. Learn more here: https://nextjs.org/telemetry +# Uncomment the following line to disable telemetry at run time +# ENV NEXT_TELEMETRY_DISABLED 1 + +# Note: Don't expose ports here, Compose will handle that for us + +# Start Next.js in development mode based on the preferred package manager +CMD \ + if [ -f yarn.lock ]; then yarn dev; \ + elif [ -f package-lock.json ]; then npm run dev; \ + elif [ -f pnpm-lock.yaml ]; then pnpm dev; \ + else npm run dev; \ + fi diff --git a/next-app/next.config.js b/next-app/next.config.js new file mode 100644 index 0000000..c10e07d --- /dev/null +++ b/next-app/next.config.js @@ -0,0 +1,6 @@ +/** @type {import('next').NextConfig} */ +const nextConfig = { + output: "standalone", +}; + +module.exports = nextConfig; diff --git a/next-app/package.json b/next-app/package.json new file mode 100644 index 0000000..2252065 --- /dev/null +++ b/next-app/package.json @@ -0,0 +1,19 @@ +{ + "private": true, + "scripts": { + "dev": "next dev", + "build": "next build", + "start": "next start" + }, + "dependencies": { + "next": "latest", + "react": "^18.2.0", + "react-dom": "^18.2.0" + }, + "devDependencies": { + "@types/node": "^18.11.9", + "@types/react": "^18.0.25", + "@types/react-dom": "^18.0.9", + "typescript": "^4.9.3" + } +} diff --git a/next-app/pnpm-lock.yaml b/next-app/pnpm-lock.yaml new file mode 100644 index 0000000..36667dc --- /dev/null +++ b/next-app/pnpm-lock.yaml @@ -0,0 +1,310 @@ +lockfileVersion: '6.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +dependencies: + next: + specifier: latest + version: 14.2.3(react-dom@18.3.1)(react@18.3.1) + react: + specifier: ^18.2.0 + version: 18.3.1 + react-dom: + specifier: ^18.2.0 + version: 18.3.1(react@18.3.1) + +devDependencies: + '@types/node': + specifier: ^18.11.9 + version: 18.19.33 + '@types/react': + specifier: ^18.0.25 + version: 18.3.3 + '@types/react-dom': + specifier: ^18.0.9 + version: 18.3.0 + typescript: + specifier: ^4.9.3 + version: 4.9.5 + +packages: + + /@next/env@14.2.3: + resolution: {integrity: sha512-W7fd7IbkfmeeY2gXrzJYDx8D2lWKbVoTIj1o1ScPHNzvp30s1AuoEFSdr39bC5sjxJaxTtq3OTCZboNp0lNWHA==} + dev: false + + /@next/swc-darwin-arm64@14.2.3: + resolution: {integrity: sha512-3pEYo/RaGqPP0YzwnlmPN2puaF2WMLM3apt5jLW2fFdXD9+pqcoTzRk+iZsf8ta7+quAe4Q6Ms0nR0SFGFdS1A==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: false + optional: true + + /@next/swc-darwin-x64@14.2.3: + resolution: {integrity: sha512-6adp7waE6P1TYFSXpY366xwsOnEXM+y1kgRpjSRVI2CBDOcbRjsJ67Z6EgKIqWIue52d2q/Mx8g9MszARj8IEA==} + engines: {node: '>= 10'} + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: false + optional: true + + /@next/swc-linux-arm64-gnu@14.2.3: + resolution: {integrity: sha512-cuzCE/1G0ZSnTAHJPUT1rPgQx1w5tzSX7POXSLaS7w2nIUJUD+e25QoXD/hMfxbsT9rslEXugWypJMILBj/QsA==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@next/swc-linux-arm64-musl@14.2.3: + resolution: {integrity: sha512-0D4/oMM2Y9Ta3nGuCcQN8jjJjmDPYpHX9OJzqk42NZGJocU2MqhBq5tWkJrUQOQY9N+In9xOdymzapM09GeiZw==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@next/swc-linux-x64-gnu@14.2.3: + resolution: {integrity: sha512-ENPiNnBNDInBLyUU5ii8PMQh+4XLr4pG51tOp6aJ9xqFQ2iRI6IH0Ds2yJkAzNV1CfyagcyzPfROMViS2wOZ9w==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@next/swc-linux-x64-musl@14.2.3: + resolution: {integrity: sha512-BTAbq0LnCbF5MtoM7I/9UeUu/8ZBY0i8SFjUMCbPDOLv+un67e2JgyN4pmgfXBwy/I+RHu8q+k+MCkDN6P9ViQ==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@next/swc-win32-arm64-msvc@14.2.3: + resolution: {integrity: sha512-AEHIw/dhAMLNFJFJIJIyOFDzrzI5bAjI9J26gbO5xhAKHYTZ9Or04BesFPXiAYXDNdrwTP2dQceYA4dL1geu8A==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [win32] + requiresBuild: true + dev: false + optional: true + + /@next/swc-win32-ia32-msvc@14.2.3: + resolution: {integrity: sha512-vga40n1q6aYb0CLrM+eEmisfKCR45ixQYXuBXxOOmmoV8sYST9k7E3US32FsY+CkkF7NtzdcebiFT4CHuMSyZw==} + engines: {node: '>= 10'} + cpu: [ia32] + os: [win32] + requiresBuild: true + dev: false + optional: true + + /@next/swc-win32-x64-msvc@14.2.3: + resolution: {integrity: sha512-Q1/zm43RWynxrO7lW4ehciQVj+5ePBhOK+/K2P7pLFX3JaJ/IZVC69SHidrmZSOkqz7ECIOhhy7XhAFG4JYyHA==} + engines: {node: '>= 10'} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: false + optional: true + + /@swc/counter@0.1.3: + resolution: {integrity: sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==} + dev: false + + /@swc/helpers@0.5.5: + resolution: {integrity: sha512-KGYxvIOXcceOAbEk4bi/dVLEK9z8sZ0uBB3Il5b1rhfClSpcX0yfRO0KmTkqR2cnQDymwLB+25ZyMzICg/cm/A==} + dependencies: + '@swc/counter': 0.1.3 + tslib: 2.6.2 + dev: false + + /@types/node@18.19.33: + resolution: {integrity: sha512-NR9+KrpSajr2qBVp/Yt5TU/rp+b5Mayi3+OlMlcg2cVCfRmcG5PWZ7S4+MG9PZ5gWBoc9Pd0BKSRViuBCRPu0A==} + dependencies: + undici-types: 5.26.5 + dev: true + + /@types/prop-types@15.7.12: + resolution: {integrity: sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==} + dev: true + + /@types/react-dom@18.3.0: + resolution: {integrity: sha512-EhwApuTmMBmXuFOikhQLIBUn6uFg81SwLMOAUgodJF14SOBOCMdU04gDoYi0WOJJHD144TL32z4yDqCW3dnkQg==} + dependencies: + '@types/react': 18.3.3 + dev: true + + /@types/react@18.3.3: + resolution: {integrity: sha512-hti/R0pS0q1/xx+TsI73XIqk26eBsISZ2R0wUijXIngRK9R/e7Xw/cXVxQK7R5JjW+SV4zGcn5hXjudkN/pLIw==} + dependencies: + '@types/prop-types': 15.7.12 + csstype: 3.1.3 + dev: true + + /busboy@1.6.0: + resolution: {integrity: sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==} + engines: {node: '>=10.16.0'} + dependencies: + streamsearch: 1.1.0 + dev: false + + /caniuse-lite@1.0.30001626: + resolution: {integrity: sha512-JRW7kAH8PFJzoPCJhLSHgDgKg5348hsQ68aqb+slnzuB5QFERv846oA/mRChmlLAOdEDeOkRn3ynb1gSFnjt3w==} + dev: false + + /client-only@0.0.1: + resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==} + dev: false + + /csstype@3.1.3: + resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} + dev: true + + /graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + dev: false + + /js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + dev: false + + /loose-envify@1.4.0: + resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} + hasBin: true + dependencies: + js-tokens: 4.0.0 + dev: false + + /nanoid@3.3.7: + resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + dev: false + + /next@14.2.3(react-dom@18.3.1)(react@18.3.1): + resolution: {integrity: sha512-dowFkFTR8v79NPJO4QsBUtxv0g9BrS/phluVpMAt2ku7H+cbcBJlopXjkWlwxrk/xGqMemr7JkGPGemPrLLX7A==} + engines: {node: '>=18.17.0'} + hasBin: true + peerDependencies: + '@opentelemetry/api': ^1.1.0 + '@playwright/test': ^1.41.2 + react: ^18.2.0 + react-dom: ^18.2.0 + sass: ^1.3.0 + peerDependenciesMeta: + '@opentelemetry/api': + optional: true + '@playwright/test': + optional: true + sass: + optional: true + dependencies: + '@next/env': 14.2.3 + '@swc/helpers': 0.5.5 + busboy: 1.6.0 + caniuse-lite: 1.0.30001626 + graceful-fs: 4.2.11 + postcss: 8.4.31 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + styled-jsx: 5.1.1(react@18.3.1) + optionalDependencies: + '@next/swc-darwin-arm64': 14.2.3 + '@next/swc-darwin-x64': 14.2.3 + '@next/swc-linux-arm64-gnu': 14.2.3 + '@next/swc-linux-arm64-musl': 14.2.3 + '@next/swc-linux-x64-gnu': 14.2.3 + '@next/swc-linux-x64-musl': 14.2.3 + '@next/swc-win32-arm64-msvc': 14.2.3 + '@next/swc-win32-ia32-msvc': 14.2.3 + '@next/swc-win32-x64-msvc': 14.2.3 + transitivePeerDependencies: + - '@babel/core' + - babel-plugin-macros + dev: false + + /picocolors@1.0.1: + resolution: {integrity: sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==} + dev: false + + /postcss@8.4.31: + resolution: {integrity: sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==} + engines: {node: ^10 || ^12 || >=14} + dependencies: + nanoid: 3.3.7 + picocolors: 1.0.1 + source-map-js: 1.2.0 + dev: false + + /react-dom@18.3.1(react@18.3.1): + resolution: {integrity: sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==} + peerDependencies: + react: ^18.3.1 + dependencies: + loose-envify: 1.4.0 + react: 18.3.1 + scheduler: 0.23.2 + dev: false + + /react@18.3.1: + resolution: {integrity: sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==} + engines: {node: '>=0.10.0'} + dependencies: + loose-envify: 1.4.0 + dev: false + + /scheduler@0.23.2: + resolution: {integrity: sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==} + dependencies: + loose-envify: 1.4.0 + dev: false + + /source-map-js@1.2.0: + resolution: {integrity: sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==} + engines: {node: '>=0.10.0'} + dev: false + + /streamsearch@1.1.0: + resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==} + engines: {node: '>=10.0.0'} + dev: false + + /styled-jsx@5.1.1(react@18.3.1): + resolution: {integrity: sha512-pW7uC1l4mBZ8ugbiZrcIsiIvVx1UmTfw7UkC3Um2tmfUq9Bhk8IiyEIPl6F8agHgjzku6j0xQEZbfA5uSgSaCw==} + engines: {node: '>= 12.0.0'} + peerDependencies: + '@babel/core': '*' + babel-plugin-macros: '*' + react: '>= 16.8.0 || 17.x.x || ^18.0.0-0' + peerDependenciesMeta: + '@babel/core': + optional: true + babel-plugin-macros: + optional: true + dependencies: + client-only: 0.0.1 + react: 18.3.1 + dev: false + + /tslib@2.6.2: + resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==} + dev: false + + /typescript@4.9.5: + resolution: {integrity: sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==} + engines: {node: '>=4.2.0'} + hasBin: true + dev: true + + /undici-types@5.26.5: + resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} + dev: true diff --git a/next-app/prod-without-multistage.Dockerfile b/next-app/prod-without-multistage.Dockerfile new file mode 100644 index 0000000..4be3076 --- /dev/null +++ b/next-app/prod-without-multistage.Dockerfile @@ -0,0 +1,48 @@ +FROM node:18-alpine + +WORKDIR /app + +# Install dependencies based on the preferred package manager +COPY package.json yarn.lock* package-lock.json* pnpm-lock.yaml* ./ +# Omit --production flag for TypeScript devDependencies +RUN \ + if [ -f yarn.lock ]; then yarn --frozen-lockfile; \ + elif [ -f package-lock.json ]; then npm ci; \ + elif [ -f pnpm-lock.yaml ]; then corepack enable pnpm && pnpm i; \ + # Allow install without lockfile, so example works even without Node.js installed locally + else echo "Warning: Lockfile not found. It is recommended to commit lockfiles to version control." && yarn install; \ + fi + +COPY src ./src +COPY public ./public +COPY next.config.js . +COPY tsconfig.json . + +# Environment variables must be present at build time +# https://github.com/vercel/next.js/discussions/14030 +ARG ENV_VARIABLE +ENV ENV_VARIABLE=${ENV_VARIABLE} +ARG NEXT_PUBLIC_ENV_VARIABLE +ENV NEXT_PUBLIC_ENV_VARIABLE=${NEXT_PUBLIC_ENV_VARIABLE} + +# Next.js collects completely anonymous telemetry data about general usage. Learn more here: https://nextjs.org/telemetry +# Uncomment the following line to disable telemetry at build time +# ENV NEXT_TELEMETRY_DISABLED 1 + +# Note: Don't expose ports here, Compose will handle that for us + +# Build Next.js based on the preferred package manager +RUN \ + if [ -f yarn.lock ]; then yarn build; \ + elif [ -f package-lock.json ]; then npm run build; \ + elif [ -f pnpm-lock.yaml ]; then pnpm build; \ + else npm run build; \ + fi + +# Start Next.js based on the preferred package manager +CMD \ + if [ -f yarn.lock ]; then yarn start; \ + elif [ -f package-lock.json ]; then npm run start; \ + elif [ -f pnpm-lock.yaml ]; then pnpm start; \ + else npm run start; \ + fi diff --git a/next-app/prod.Dockerfile b/next-app/prod.Dockerfile new file mode 100644 index 0000000..eca0431 --- /dev/null +++ b/next-app/prod.Dockerfile @@ -0,0 +1,73 @@ +FROM node:18-alpine AS base + +# Step 1. Rebuild the source code only when needed +FROM base AS builder + +WORKDIR /app + +# Install dependencies based on the preferred package manager +COPY package.json yarn.lock* package-lock.json* pnpm-lock.yaml* ./ +# Omit --production flag for TypeScript devDependencies +RUN \ + if [ -f yarn.lock ]; then yarn --frozen-lockfile; \ + elif [ -f package-lock.json ]; then npm ci; \ + elif [ -f pnpm-lock.yaml ]; then corepack enable pnpm && pnpm i; \ + # Allow install without lockfile, so example works even without Node.js installed locally + else echo "Warning: Lockfile not found. It is recommended to commit lockfiles to version control." && yarn install; \ + fi + +COPY src ./src +COPY public ./public +COPY next.config.js . +COPY tsconfig.json . + +# Environment variables must be present at build time +# https://github.com/vercel/next.js/discussions/14030 +ARG ENV_VARIABLE +ENV ENV_VARIABLE=${ENV_VARIABLE} +ARG NEXT_PUBLIC_ENV_VARIABLE +ENV NEXT_PUBLIC_ENV_VARIABLE=${NEXT_PUBLIC_ENV_VARIABLE} + +# Next.js collects completely anonymous telemetry data about general usage. Learn more here: https://nextjs.org/telemetry +# Uncomment the following line to disable telemetry at build time +# ENV NEXT_TELEMETRY_DISABLED 1 + +# Build Next.js based on the preferred package manager +RUN \ + if [ -f yarn.lock ]; then yarn build; \ + elif [ -f package-lock.json ]; then npm run build; \ + elif [ -f pnpm-lock.yaml ]; then pnpm build; \ + else npm run build; \ + fi + +# Note: It is not necessary to add an intermediate step that does a full copy of `node_modules` here + +# Step 2. Production image, copy all the files and run next +FROM base AS runner + +WORKDIR /app + +# Don't run production as root +RUN addgroup --system --gid 1001 nodejs +RUN adduser --system --uid 1001 nextjs +USER nextjs + +COPY --from=builder /app/public ./public + +# Automatically leverage output traces to reduce image size +# https://nextjs.org/docs/advanced-features/output-file-tracing +COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./ +COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static + +# Environment variables must be redefined at run time +ARG ENV_VARIABLE +ENV ENV_VARIABLE=${ENV_VARIABLE} +ARG NEXT_PUBLIC_ENV_VARIABLE +ENV NEXT_PUBLIC_ENV_VARIABLE=${NEXT_PUBLIC_ENV_VARIABLE} + +# Uncomment the following line to disable telemetry at run time +# ENV NEXT_TELEMETRY_DISABLED 1 + +# Note: Don't expose ports here, Compose will handle that for us + +CMD ["node", "server.js"] diff --git a/next-app/public/favicon.ico b/next-app/public/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..4965832f2c9b0605eaa189b7c7fb11124d24e48a GIT binary patch literal 15086 zcmeHOOH5Q(7(R0cc?bh2AT>N@1PWL!LLfZKyG5c!MTHoP7_p!sBz0k$?pjS;^lmgJ zU6^i~bWuZYHL)9$wuvEKm~qo~(5=Lvx5&Hv;?X#m}i|`yaGY4gX+&b>tew;gcnRQA1kp zBbm04SRuuE{Hn+&1wk%&g;?wja_Is#1gKoFlI7f`Gt}X*-nsMO30b_J@)EFNhzd1QM zdH&qFb9PVqQOx@clvc#KAu}^GrN`q5oP(8>m4UOcp`k&xwzkTio*p?kI4BPtIwX%B zJN69cGsm=x90<;Wmh-bs>43F}ro$}Of@8)4KHndLiR$nW?*{Rl72JPUqRr3ta6e#A z%DTEbi9N}+xPtd1juj8;(CJt3r9NOgb>KTuK|z7!JB_KsFW3(pBN4oh&M&}Nb$Ee2 z$-arA6a)CdsPj`M#1DS>fqj#KF%0q?w50GN4YbmMZIoF{e1yTR=4ablqXHBB2!`wM z1M1ke9+<);|AI;f=2^F1;G6Wfpql?1d5D4rMr?#f(=hkoH)U`6Gb)#xDLjoKjp)1;Js@2Iy5yk zMXUqj+gyk1i0yLjWS|3sM2-1ECc;MAz<4t0P53%7se$$+5Ex`L5TQO_MMXXi04UDIU+3*7Ez&X|mj9cFYBXqM{M;mw_ zpw>azP*qjMyNSD4hh)XZt$gqf8f?eRSFX8VQ4Y+H3jAtvyTrXr`qHAD6`m;aYmH2zOhJC~_*AuT} zvUxC38|JYN94i(05R)dVKgUQF$}#cxV7xZ4FULqFCNX*Forhgp*yr6;DsIk=ub0Hv zpk2L{9Q&|uI^b<6@i(Y+iSxeO_n**4nRLc`P!3ld5jL=nZRw6;DEJ*1z6Pvg+eW|$lnnjO zjd|8>6l{i~UxI244CGn2kK@cJ|#ecwgSyt&HKA2)z zrOO{op^o*- + + \ No newline at end of file diff --git a/next-app/src/pages/_app.tsx b/next-app/src/pages/_app.tsx new file mode 100644 index 0000000..42e2aed --- /dev/null +++ b/next-app/src/pages/_app.tsx @@ -0,0 +1,6 @@ +import "../styles/globals.css"; +import type { AppProps } from "next/app"; + +export default function MyApp({ Component, pageProps }: AppProps) { + return ; +} diff --git a/next-app/src/pages/index.tsx b/next-app/src/pages/index.tsx new file mode 100644 index 0000000..10b7287 --- /dev/null +++ b/next-app/src/pages/index.tsx @@ -0,0 +1,70 @@ +import Head from "next/head"; +import Image from "next/image"; +import styles from "../styles/Home.module.css"; + +export default function Home() { + return ( + + ); +} diff --git a/next-app/src/styles/Home.module.css b/next-app/src/styles/Home.module.css new file mode 100644 index 0000000..09497fa --- /dev/null +++ b/next-app/src/styles/Home.module.css @@ -0,0 +1,137 @@ +.container { + min-height: 100vh; + padding: 0 0.5rem; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; +} + +.main { + padding: 5rem 0; + flex: 1; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; +} + +.footer { + width: 100%; + height: 100px; + border-top: 1px solid #eaeaea; + display: flex; + justify-content: center; + align-items: center; +} + +.footer img { + margin-left: 0.5rem; +} + +.footer a { + display: flex; + justify-content: center; + align-items: center; +} + +.title a { + color: #0070f3; + text-decoration: none; +} + +.title a:hover, +.title a:focus, +.title a:active { + text-decoration: underline; +} + +.title { + margin: 0; + line-height: 1.15; + font-size: 4rem; +} + +.subtitle { + margin: 0; + line-height: 1.15; + font-size: 2rem; +} + +.title, +.description { + text-align: center; +} + +.description { + line-height: 1.5; + font-size: 1.5rem; +} + +.code { + background: #fafafa; + border-radius: 5px; + padding: 0.75rem; + font-size: 1.1rem; + font-family: + Menlo, + Monaco, + Lucida Console, + Liberation Mono, + DejaVu Sans Mono, + Bitstream Vera Sans Mono, + Courier New, + monospace; +} + +.grid { + display: flex; + align-items: center; + justify-content: center; + flex-wrap: wrap; + max-width: 800px; + margin-top: 3rem; +} + +.card { + margin: 1rem; + flex-basis: 45%; + padding: 1.5rem; + text-align: left; + color: inherit; + text-decoration: none; + border: 1px solid #eaeaea; + border-radius: 10px; + transition: + color 0.15s ease, + border-color 0.15s ease; +} + +.card:hover, +.card:focus, +.card:active { + color: #0070f3; + border-color: #0070f3; +} + +.card h3 { + margin: 0 0 1rem 0; + font-size: 1.5rem; +} + +.card p { + margin: 0; + font-size: 1.25rem; + line-height: 1.5; +} + +.logo { + height: 1em; +} + +@media (max-width: 600px) { + .grid { + width: 100%; + flex-direction: column; + } +} diff --git a/next-app/src/styles/globals.css b/next-app/src/styles/globals.css new file mode 100644 index 0000000..51a2a4e --- /dev/null +++ b/next-app/src/styles/globals.css @@ -0,0 +1,26 @@ +html, +body { + padding: 0; + margin: 0; + font-family: + -apple-system, + BlinkMacSystemFont, + Segoe UI, + Roboto, + Oxygen, + Ubuntu, + Cantarell, + Fira Sans, + Droid Sans, + Helvetica Neue, + sans-serif; +} + +a { + color: inherit; + text-decoration: none; +} + +* { + box-sizing: border-box; +} diff --git a/next-app/tsconfig.json b/next-app/tsconfig.json new file mode 100644 index 0000000..b8d5978 --- /dev/null +++ b/next-app/tsconfig.json @@ -0,0 +1,20 @@ +{ + "compilerOptions": { + "target": "es5", + "lib": ["dom", "dom.iterable", "esnext"], + "allowJs": true, + "skipLibCheck": true, + "strict": false, + "forceConsistentCasingInFileNames": true, + "noEmit": true, + "esModuleInterop": true, + "module": "esnext", + "moduleResolution": "node", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "preserve", + "incremental": true + }, + "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"], + "exclude": ["node_modules"] +} diff --git a/readme.md b/readme.md index e69de29..f43adce 100644 --- a/readme.md +++ b/readme.md @@ -0,0 +1,101 @@ +# With Docker Compose + +This example contains everything needed to get a Next.js development and production environment up and running with Docker Compose. + +## Benefits of Docker Compose + +- Develop locally without Node.js or TypeScript installed ✨ +- Easy to run, consistent development environment across macOS, Windows, and Linux teams +- Run multiple Next.js apps, databases, and other microservices in a single deployment +- Multistage builds combined with [Output Standalone](https://nextjs.org/docs/advanced-features/output-file-tracing#automatically-copying-traced-files) outputs up to 85% smaller apps (Approximately 110 MB compared to 1 GB with create-next-app) +- Easy configuration with YAML files + +## How to use + +Execute [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app) with [npm](https://docs.npmjs.com/cli/init), [Yarn](https://yarnpkg.com/lang/en/docs/cli/create/), or [pnpm](https://pnpm.io) to bootstrap the example: + +```bash +npx create-next-app --example with-docker-compose with-docker-compose-app +``` + +```bash +yarn create next-app --example with-docker-compose with-docker-compose-app +``` + +```bash +pnpm create next-app --example with-docker-compose with-docker-compose-app +``` + +Optionally, after the installation is complete: + +- Run `cd next-app`, then run `npm install` or `yarn install` or `pnpm install` to generate a lockfile. + +It is recommended to commit a lockfile to version control. Although the example will work without one, build errors are more likely to occur when using the latest version of all dependencies. This way, we're always using a known good configuration to develop and run in production. + +## Prerequisites + +Install [Docker Desktop](https://docs.docker.com/get-docker) for Mac, Windows, or Linux. Docker Desktop includes Docker Compose as part of the installation. + +## Development + +First, run the development server: + +```bash +# Create a network, which allows containers to communicate +# with each other, by using their container name as a hostname +docker network create my_network + +# Build dev +docker compose -f docker-compose.dev.yml build + +# Up dev +docker compose -f docker-compose.dev.yml up +``` + +Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. + +You can start editing the page by modifying `pages/index.tsx`. The page auto-updates as you edit the file. + +## Production + +Multistage builds are highly recommended in production. Combined with the Next [Output Standalone](https://nextjs.org/docs/advanced-features/output-file-tracing#automatically-copying-traced-files) feature, only `node_modules` files required for production are copied into the final Docker image. + +First, run the production server (Final image approximately 110 MB). + +```bash +# Create a network, which allows containers to communicate +# with each other, by using their container name as a hostname +docker network create my_network + +# Build prod +docker compose -f docker-compose.prod.yml build + +# Up prod in detached mode +docker compose -f docker-compose.prod.yml up -d +``` + +Alternatively, run the production server without multistage builds (Final image approximately 1 GB). + +```bash +# Create a network, which allows containers to communicate +# with each other, by using their container name as a hostname +docker network create my_network + +# Build prod without multistage +docker compose -f docker-compose.prod-without-multistage.yml build + +# Up prod without multistage in detached mode +docker compose -f docker-compose.prod-without-multistage.yml up -d +``` + +Open [http://localhost:3000](http://localhost:3000). + +## Useful commands + +```bash +# Stop all running containers +docker kill $(docker ps -aq) && docker rm $(docker ps -aq) + +# Free space +docker system prune -af --volumes +```