Maintance mode w aplikacjach Next.js

Autor Agnieszka Wojtas

Featured image

Podczas tworzenia i zarządzania projektem ważne jest uwzględnienie momentów, kiedy aplikacja będzie niedostępna z powodu konserwacji. W przypadku systemów CMS, takich jak np. WordPress, istnieją rozmaite wtyczki, umożliwiające zasłonę witryny podczas wprowadzania zmian lub testowania nowych funkcjonalności.

Ale jak zaimplementować maintenance mode w Next.js? Czy jest to równie proste, co kilkuminutowa konfiguracja wtyczki w WordPress’ie?

Oczywiście, że tak!

Wyzwanie

Podczas poszukiwań rozwiązania dla trybu konserwacji w aplikacjach Next.js natknęłam się na brak dostępnych narzędzi, które spełniałyby moje oczekiwania pod względem szybkości, elastyczności i łatwości użycia.

Rozwiązanie

Na początek tworzymy plik route.ts w głównym folderze aplikacji app->api, który będzie odpowiedzialny za obsługę żądania dla naszego maintenance mode:

import { serialize } from "cookie";

export async function POST(request: Request, params: { slug: string }) {
 const data: { password: string } = await request.json();
 const password = data.password;

 const expirationTime = 4 * 60 * 60; // 4 hours
 const cookie = serialize(process.env.PASSWORD_COOKIE_NAME!, "true", {
   httpOnly: true,
   path: "/",
   maxAge: expirationTime,
 });

 if (process.env.PAGE_PASSWORD !== password) {
   return new Response("incorrect password", {
     status: 401,
   });
 }

 return new Response("password correct", {
   status: 200,
   headers: {
     "Set-Cookie": cookie,
   },
 });
}

Kolejnym krokiem będzie stworzenie komponentu, którego wygląd, użytkownik zobaczy bezpośrednio po wejściu na stronę. Poniżej przykładowy wzór, który można dostosować do potrzeb klienta:

"use client";

import React, { useState, useEffect } from "react";

const PasswordPromptDialog = () => {
 const [password, setPassword] = useState("");
 const [passwordIncorrect, setPasswordIncorrect] = useState(false);
 const [noPassword, setNoPassword] = useState(false);

 const handleSubmit = async (e: React.FormEvent) => {
   e.preventDefault();

   const request = await fetch(`/api`, {
     body: JSON.stringify({ password }),
     headers: { "Content-Type": "application/json" },
     method: "post",
   });

   if (password) {
     if (request.status !== 200) {
       return setNoPassword(false), setPasswordIncorrect(true);
     } else {
       window.location.reload();
     }
   } else {
     return setPasswordIncorrect(false), setNoPassword(true);
   }
 };

 useEffect(() => {
   setPassword("");
 }, [passwordIncorrect]);

 return (
   <div>
     <p>Maintenance mode is enabled!</p>
     <div>
       <form onSubmit={handleSubmit}>
         <label htmlFor="password">Password:</label>
         <input
           type="password"
           id="password"
           value={password}
           onChange={(e) => setPassword(e.target.value)}
         />
         <button type="submit">
           Log In
         </button>
       </form>
       {noPassword && (
         <p
           style={{
             color: "red",
             fontSize: "14px",
           }}
         >
           Enter the password.
         </p>
       )}
       {passwordIncorrect && (
         <p
           style={{
             color: "red",
             fontSize: "14px",
           }}
         >
           The entered password is incorrect.
         </p>
       )}
     </div>
   </div>
 );
};

export default PasswordPromptDialog;

Ostatnim krokiem, będzie wywołanie komponentu PasswordPromptDialog w pliku layout.tsx w następujący sposób:

  • w celu wyświetlenia zaślepki, sprawdzamy wartość zmiennej środowiskowej MAINTENANCE_MODE,
  • jeśli tryb konserwacji ma być włączony, weryfikujemy zawartość cookies w celu ustalenia autentykacji użytkownika i wówczas wyświetlamy naszą zaślepkę — w przeciwnym razie, wyświetlany jest domyślny content strony.
import { cookies } from "next/headers";
import PasswordPromptDialog from "@/components/PasswordPromptDialog/PasswordPromptDialog";

import "@/styles/globals.scss";

export default async function RootLayout({
 children,
}: {
 children: React.ReactNode;
}) {
 const isMaintenanceMode = process.env.MAINTENANCE_MODE === "true";

 if (isMaintenanceMode) {
   const cookieStore = cookies();
   const loginCookies = cookieStore.get(
     `${process.env.PASSWORD_COOKIE_NAME!}`
   );
   const isLoggedIn = !!loginCookies?.value;

   if (!isLoggedIn) {
     return (
       <html lang="pl">
         <body>
           <PasswordPromptDialog />;
         </body>
       </html>
     );
   }
 }

 return (
   <html lang="pl">
     <body>{children}</body>
   </html>
 );
}

Na sam koniec, wystarczy jeszcze dodać poniższe zmienne środowiskowe do projektu (.env) z ustalonymi wartościami:

MAINTENANCE_MODE=FALSE
PASSWORD_COOKIE_NAME=authorized
SEARCH_QUERY_NAME=password
PAGE_PASSWORD=YourPasswordForWebsite

Podsumowanie

Wdrożenie trybu konserwacji w aplikacjach Next.js wbrew pozorom jest kluczowe podczas tworzenia i zarządzania projektem. Choć systemy CMS, takie jak WordPress, oferują różnorodne wtyczki umożliwiające zasłonięcie witryny podczas prac konserwacyjnych, implementacja maintenance mode w Next.js może wydawać się bardziej wymagająca.

Jednakże, dzięki przedstawionemu rozwiązaniu, które opiera się na wykorzystaniu cookies do przechowywania informacji o autentykacji, tworzenie trybu konserwacji staje się równie prostą operacją, co konfiguracja wtyczki w systemie CMS. Dzięki zastosowaniu cookies, nie tylko zachowujemy elastyczność i szybkość działania, ale również zwiększamy poziom bezpieczeństwa poprzez uniemożliwienie odczytu danych przez skrypty JavaScript (ustawienie opcji httpOnly).

Podsumowując, powyższe rozwiązanie pozwala na szybką i łatwą implementację trybu konserwacji w aplikacjach Next.js, jednocześnie dając użytkownikowi pełną kontrolę nad wyglądem "zasłony" i autentykacją. Dodatkowo, możliwość szybkiego przełączania trybu konserwacji za pomocą zmiany wartości zmiennej środowiskowej sprawia, że nasze rozwiązanie jest niezwykle elastyczne i dostosowane do różnych potrzeb projektowych.

Inne posty na blogu

Medusa vs Magento: Całkowity koszt posiadania

Magento, w porównaniu do Medusy, może prowadzić do wyższych kosztów długoterminowych z powodu swojej licencji oraz ryzyka związanego ze stopniowym spadkiem popularności języka PHP...

Medusa vs Magento: Porównanie Wydajności

To porównanie ma na celu sprawdzenie, czy Magento, może równać się z wydajnością platform zbudowanych z myślą o API od samego początku...

Opowiedz nam o swoim projekcie

Myślisz o nowym projekcie? Zrealizujmy go!

Naciskając „Wyślij wiadomość” udzielasz nam, tj. Rigby, zgody na email marketing naszych usług w ramach komunikacji dotyczącej Twojego projektu. Zgodę możesz wycofać, np. pisząc na adres hello@rigbyjs.com.
Więcej
placeholder

Grzegorz Tomaka

Co-CEO & Co-founder

LinkedIn icon
placeholder

Jakub Zbaski

Co-CEO & Co-founder

LinkedIn icon