From 0cf23cacb48f2a2e2e5a601409826eff0f277388 Mon Sep 17 00:00:00 2001 From: renanleonel Date: Sat, 20 Apr 2024 17:30:53 -0300 Subject: [PATCH 1/3] fix: timezone --- src/lib/utils.ts | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/lib/utils.ts b/src/lib/utils.ts index 016b865..cee8335 100644 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -9,18 +9,19 @@ export function formatDate(date: string) { let currentDate = new Date(); let targetDate = new Date(date); - let yearsAgo = currentDate.getFullYear() - targetDate.getFullYear(); - let monthsAgo = currentDate.getMonth() - targetDate.getMonth(); - let daysAgo = currentDate.getDate() - targetDate.getDate(); + let timezoneOffset = targetDate.getTimezoneOffset() * 60 * 1000; + targetDate = new Date(targetDate.getTime() + timezoneOffset); + + let timeDifference = currentDate.getTime() - targetDate.getTime(); + + let daysAgo = Math.floor(timeDifference / (1000 * 60 * 60 * 24)); let formattedDate = ""; - if (yearsAgo > 0) { - formattedDate = `${yearsAgo}y ago`; - } else if (monthsAgo > 0) { - formattedDate = `${monthsAgo}mo ago`; - } else if (daysAgo > 0) { + if (daysAgo > 1) { formattedDate = `${daysAgo}d ago`; + } else if (daysAgo === 1) { + formattedDate = "Yesterday"; } else { formattedDate = "Today"; } From 0300385587805d3da58807cda17f8ba904b76893 Mon Sep 17 00:00:00 2001 From: renanleonel Date: Sat, 20 Apr 2024 17:31:03 -0300 Subject: [PATCH 2/3] fix: unused tags --- src/content/config.ts | 5 ----- src/content/posts/authentication.mdx | 3 --- 2 files changed, 8 deletions(-) diff --git a/src/content/config.ts b/src/content/config.ts index 7d5c3cd..8883e5f 100644 --- a/src/content/config.ts +++ b/src/content/config.ts @@ -7,11 +7,6 @@ const postsCollection = defineCollection({ pubDate: z.date(), description: z.string(), author: z.string(), - image: z.object({ - url: z.string(), - alt: z.string(), - }), - tags: z.array(z.string()), }), }); diff --git a/src/content/posts/authentication.mdx b/src/content/posts/authentication.mdx index d51ac3a..8a2d3dd 100644 --- a/src/content/posts/authentication.mdx +++ b/src/content/posts/authentication.mdx @@ -3,9 +3,6 @@ title: "Next.js 14 authentication" pubDate: 2024-02-26 description: "Creating an authentication flow in Next.js 14 using NextAuth v5" author: "Renan Leonel" -image: - url: "https://docs.astro.build/assets/full-logo-light.png" - alt: "The full Astro logo." tags: ["Next.js", "NextAuth", "Authentication"] --- From 9401cf57e5f043d2c2b2a774e53a5d4aba7412d3 Mon Sep 17 00:00:00 2001 From: renanleonel Date: Sun, 21 Apr 2024 08:53:05 -0300 Subject: [PATCH 3/3] feat: dockerizing nextjs --- src/content/posts/dockerizing-nextjs.mdx | 403 +++++++++++++++++++++++ 1 file changed, 403 insertions(+) create mode 100644 src/content/posts/dockerizing-nextjs.mdx diff --git a/src/content/posts/dockerizing-nextjs.mdx b/src/content/posts/dockerizing-nextjs.mdx new file mode 100644 index 0000000..752bb5d --- /dev/null +++ b/src/content/posts/dockerizing-nextjs.mdx @@ -0,0 +1,403 @@ +--- +title: "Dockerizing Next.js" +pubDate: 2024-04-20 +description: "Step-by-step guide to containerize a Next.js application with Docker" +author: "Renan Leonel" +--- + +This tutorial will cover the step-by-step process of containerizing a Next.js application using Docker. We will follow these steps: + +1. **[Installing Necessary Tools](#installing-necessary-tools)**: For this tutorial, we will need WSL, Docker, Docker Compose, and a Next.js project. +2. **[Dockerfile](#dockerfile)**: We will create two Dockerfiles, one for development environment and the other for production environment. +3. **[Docker Compose](#docker-compose)**: We will create a Docker Compose file to manage Docker containers. +4. **[Publishing to Docker Hub](#publishing-to-docker-hub)**: We will publish the image to Docker Hub to make it available for others to use. +5. **[Docker Cheat Sheet](#docker-cheat-sheet)**: A list of useful commands for managing Docker containers. + +You can find the source code for this tutorial on [GitHub](https://github.com/renanleonel/demo-docker-nextjs) + +You can also find this article on [DEV](https://dev.to/renao/dockerizing-nextjs-2opd) and [Medium](https://medium.com/@renanleonel/dockerizing-next-js-a3d9c51c0182). + +### Installing Necessary Tools + +This tutorial assumes that you're using Windows as your operating system. If you're using a different operating system, the commands may vary. + +Firstly, we need to install WSL (Windows Subsystem for Linux), which is a feature of Windows that allows you to run a Linux environment directly on your Windows computer without the need for a virtual machine. To do this, open PowerShell as an administrator and execute the following command: + +```bash +wsl --install +``` + +If you encounter any issues during installation, you can refer to the [official tutorial](https://docs.microsoft.com/en-us/windows/wsl/install) for WSL installation provided by Microsoft. + +Now, we can proceed to install Docker and Docker Compose. + +Docker is a virtualization platform that encapsulates applications in isolated containers, providing consistency and efficiency in development. This allows the application to run autonomously and consistently in any environment, eliminating conflicts between different software versions. + +For installation, follow the comprehensive tutorials created by Digital Ocean for [Docker](https://www.digitalocean.com/community/tutorials/how-to-install-and-use-docker-on-ubuntu-22-04) and [Docker Compose](https://www.digitalocean.com/community/tutorials/how-to-install-and-use-docker-compose-on-ubuntu-22-04) on Ubuntu. + +Finally, create a Next.js application using the command: `npx create-next-app@latest` or use an existing project. For this tutorial, we will use [this template repository](https://github.com/renanleonel/nextjs-template) that I use for all my projects, using pnpm as the package manager. + +### Dockerfile + +The Dockerfile is a configuration file that contains all the necessary commands to build an image. For this tutorial, we will create two files: one for the development environment and another for the production environment. + +First, create a `Dockerfile.prod` file in the root directory of your project. Then, add the following code: + +```yaml +FROM node:20 AS base +WORKDIR /app +RUN npm i -g pnpm +COPY package.json pnpm-lock.yaml ./ + +RUN pnpm install + +COPY . . +RUN pnpm build + +FROM node:20-alpine3.19 as release +WORKDIR /app +RUN npm i -g pnpm + +COPY --from=base /app/node_modules ./node_modules +COPY --from=base /app/package.json ./package.json +COPY --from=base /app/.next ./.next + +EXPOSE 3000 + +CMD ["pnpm", "start"] +``` + +Now, let's analyze the commands in the `Dockerfile.prod` file: + +```yaml +# Define the base image as node:20 and name it as base. +FROM node:20 AS base + +# Set the working directory inside the container to /app. +# We need to set the working directory so Docker knows where to run the commands. +WORKDIR /app + +# Globally install the package manager pnpm. +RUN npm i -g pnpm + +# Copy the package.json and pnpm-lock.yaml files to the working directory in the container. +# This command is necessary for Docker to install project dependencies. +COPY package.json pnpm-lock.yaml ./ + +# Install project dependencies using pnpm. +RUN pnpm install + +# Copy all files from the context directory (where the Dockerfile is located) to the working directory in the container. +COPY . . + +# Run the project build command using pnpm. +RUN pnpm build + +# Define a second stage of the image based on node:20-alpine3.19 and name it as release. +# Alpine image is a lighter version of node, which helps reduce the final image size. +FROM node:20-alpine3.19 as release + +# Set the working directory inside the container to /app. +WORKDIR /app + +# Globally install the package manager pnpm. +RUN npm i -g pnpm + +# Copy the node_modules folder from the base stage to the node_modules directory in the release stage. +COPY --from=base /app/node_modules ./node_modules + +# Copy the package.json file from the base stage to the current directory in the release stage. +COPY --from=base /app/package.json ./package.json + +# Copy the .next folder from the base stage to the .next directory in the release stage. +COPY --from=base /app/.next ./.next + + +# Define the default command to be executed when the container is started with pnpm start. +CMD ["pnpm", "start"] +``` + +Next, create a `Dockerfile.dev` file in the root of your project. + +```yaml +FROM node:20 AS base +WORKDIR /app +RUN npm i -g pnpm +COPY package.json pnpm-lock.yaml ./ + +RUN pnpm install + +COPY . . + +FROM node:20-alpine3.19 as release +WORKDIR /app +RUN npm i -g pnpm + +COPY --from=base /app/node_modules ./node_modules +COPY --from=base /app/package.json ./package.json +COPY --from=base /app/.next ./.next +COPY --from=base /app/src ./src +COPY --from=base /app . + +EXPOSE 3000 + +CMD ["pnpm", "dev"] +``` + +We can test if the Dockerfile is working correctly by building our application using the command: + +```bash +docker build -t nextjs:v1 -f Dockerfile.prod . +``` + +To execute, use the command: + +```bash +docker run -p 3000:3000 nextjs:v1 +``` + +It's worth highlighting the meaning of the flags used: + +- `-t`: This flag is used to tag an image. +- `-f`: This flag specifies the Dockerfile to use for building an image. +- `-p`: This flag maps ports between the Docker container and the host system. + +Upon accessing http://localhost:3000, you should see the Next.js application running inside a Docker container. You can also verify if the container is running correctly by executing the command: + +```bash +docker ps +``` + +If everything is working correctly, you should see something like this: + +```bash +CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES +88c1975e087b nextjs:v1 "docker-entrypoint.s…" 7 seconds ago Up 6 seconds 0.0.0.0:3000->3000/tcp, :::3000->3000/tcp festive_leavitt +``` + +However, hot reload isn't working when using the `Dockerfile.dev` because the container is not watching for changes in the files. + +Furthermore, manually executing these commands every time we want to run the application is a tedious process. To address these issues, let's use Docker Compose. + +### Docker Compose + +Docker Compose is a tool that simplifies the management of multiple Docker containers, allowing you to define and run them all with a single command, simplifying the configuration and management of multi-container applications. + +Additionally, we have access to the concept of volumes, which allow data persistence between containers and the host, ensuring that data is not lost when containers are restarted or removed and allowing changes in files to be reflected in the container in real-time. Furthermore, Docker Compose allows the definition of environment variables in a `.env` file to facilitate the configuration of the application. + +To use Docker Compose, we need to create a configuration file called `docker-compose.yml`. This file defines the services that will be run by Docker Compose, as well as the configurations for each service. + +```yaml +version: "3.7" + +services: + dev: + build: + context: . + dockerfile: Dockerfile.dev + container_name: demo-docker-nextjs-dev + environment: + - WATCHPACK_POLLING=true + volumes: + - .:/app + - /app/node_modules + - /app/.next + ports: + - "3000:3000" + env_file: + - .env.local + prod: + build: + context: . + dockerfile: Dockerfile.prod + container_name: demo-docker-nextjs + ports: + - "3000:3000" + env_file: + - .env.local + +volumes: + node_modules: +``` + +Let's analyze the commands in the `docker-compose.yml` file: + +```yaml +# Define the version of Docker Compose. +version: "3.7" + +# Define the services to be run by Docker Compose. +services: + # Define the dev service. + # This service will be used to run the application in the development environment. + dev: + # Define the build context as the current directory and the Dockerfile to be used as Dockerfile.dev. + build: + context: . + dockerfile: Dockerfile.dev + # Define the container name. + container_name: demo-docker-nextjs-dev + # Define the environment variable WATCHPACK_POLLING as true. This is necessary for hot reload to work correctly. + environment: + - WATCHPACK_POLLING=true + # Define the volumes to be mounted in the container. + # The volume .:/app maps the current working directory to the /app directory in the container. + # The volume /app/node_modules is used to persist project dependencies between containers. + # The volume /app/.next is used to persist files generated by Next.js between containers. + volumes: + - .:/app + - /app/node_modules + - /app/.next + # Maps the ports to be exposed on the host. + ports: + - "3000:3000" + # Define the environment file to be used. + env_file: + - .env.local + # Define the prod service. + # This service will be used to run the application in the production environment. + prod: + # Define the build context as the current directory and the Dockerfile to be used as Dockerfile.prod. + build: + context: . + dockerfile: Dockerfile.prod + # Define the container name. + container_name: demo-docker-nextjs + # Maps the ports to be exposed on the host. + ports: + - "3000:3000" + # Define the environment file to be used. + env_file: + - .env.local + +# Define the volumes to be used. +# The node_modules volume is used to persist project dependencies between containers. +volumes: + node_modules: +``` + +To run the application using Docker Compose, execute the command: + +```bash +docker-compose up prod +``` + +Or, in development mode: + +```bash +docker-compose up dev +``` + +Now, upon accessing `http://localhost:3000`, you should see the Next.js application running inside a Docker container with hot reload functioning in the development environment. Try making a change to any file and observe the changes being reflected in the container in real-time. + +### Publishing to Docker Hub + +Finally, we can upload our application to [Docker Hub](https://hub.docker.com/) so that other people can use the image we created. To do this, follow the steps below: + +1. Log in to Docker Hub using the command: + +```bash +docker login +``` + +2. Build the image using the command: + +```bash +docker build -t /: -f . +``` + +In my case, the command would be: + +```bash +docker build -t renanleonel/demo-docker-nextjs-prod:v1 -f Dockerfile.prod . +``` + +3. Push the image to Docker Hub using the command: + +```bash +docker push /: +``` + +We can view the image through the [link](https://hub.docker.com/r/renanleonel/demo-docker-nextjs-prod) on Docker Hub. To run the image in any environment that supports Docker, we should download the image using the command: + +```bash +docker push renanleonel/demo-docker-nextjs-prod:v1 +``` + +and then run the container: + +```bash +docker run -p 3000:3000 renanleonel/demo-docker-nextjs-prod:v1 +``` + +Now your Next.js application is containerized and ready to be used in any environment that supports Docker. + +### Docker Cheat Sheet + +Here are some useful commands for managing Docker containers: + +- View all running containers + +```bash +docker ps +``` + +- View all containers, including stopped ones + +```bash +docker ps -a +``` + +- To stop a container, execute the command + +```bash +docker stop +``` + +- Stop all running Docker containers + +```bash +docker stop $(docker ps -a -q) +``` + +- To remove a container, execute the command + +```bash +docker rm +``` + +- Remove all Docker containers + +```bash +docker rm $(docker ps -a -q) +``` + +- Remove all stopped Docker containers + +```bash +docker container prune +``` + +- To view the images on your system, execute the command + +```bash +docker image ls +``` + +- To remove an image, execute the command + +```bash +docker image rm : +``` + +- If the image is being used by a container, you must stop and remove the container before removing the image. Another way is to add the -f flag to force removal. + +```bash +docker image rm -f : +``` + +- To remove all images, execute the command + +```bash +docker rmi $(docker images -q) +```