برنامه نویسی

گام به گام: اپلیکیشن Multi-Tenant با Next.js

Next.js اکنون به شما این امکان را می دهد که به راحتی با استفاده از زیر دامنه ها یک برنامه چند مستاجر ایجاد کنید. این به شما امکان می‌دهد تا برنامه‌های وب مانند Linktree، Super.so و سایر برنامه‌ها را ایجاد کنید که به عنوان مثال کاربر صفحه وب خود را دریافت می‌کند.

قبل از شروع، در اینجا چند منبع اضافی وجود دارد:

مرحله 1: یک برنامه Next.js خالی ایجاد کنید

npx create-next-app

از شما پرسیده می شود که آیا Typescript، ESLint و گزینه های دیگر را می خواهید. برای همه چیز بله را بزنید.

پس از ایجاد برنامه، آن را در ویرایشگر کد خود (VSCode) باز کنید.

مرحله 2: استقرار برنامه در Vercel

می توانید با باز کردن ترمینال (Command + J در مک) داخل VSCode را انجام دهید.

Vercel CLI را نصب کنید:
npm i -g vercel

پس از انجام این کار، ادامه دهید و اجرا کنید:
vercel --prod تا آن را در تولید مستقر کند

روی پیوند استقرار کلیک کنید تا در داشبورد Vercel قرار بگیرید.

مرحله 3: دامنه خود را راه اندازی کنید

برای استفاده از ویژگی زیر دامنه در Vercel، باید دامنه wildcard خود را تنظیم کنید. اگر شما یک توسعه دهنده هستید، باید دامنه های استفاده نشده زیادی داشته باشید 😉

مال من buildwithnext.com نام دارد

من از Namecheap استفاده می کنم، بنابراین در اینجا نحوه انجام آن به شرح زیر است:

در داشبورد Vercel دامنه نویسه خود را اضافه کنید
افزودن یک دامنه عام در Vercel

سپس وارد حساب Namecheap خود شوید و آدرس های DNS Nameserver زیر را اضافه کنید

DNS سرور نام سفارشی را در Namecheap تنظیم کنید

هنگامی که یک دامنه حروف اضافه می کنید، Vercel به طور خودکار همه دامنه های دیگر را برای شما پر می کند.

مرحله 4: راه اندازی مسیریابی در Next.js

یک پوشه جدید در پوشه pages به نام اضافه کنید _sites و سپس یکی دیگر در آنجا زنگ زد [site]. سپس یک فایل index.tsx را به آن اضافه کنید [site] پوشه

ساختار پوشه جدید شما اکنون باید به شکل زیر باشد:

pages
└───api
└───sites
│      │
│    [site]
│      │  index.tsx
package.json
etc
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

داخل فایل index.tsx کد زیر را اضافه کنید:

import { useRouter } from "next/router";
// we will create these in the next step
import { getHostnameDataBySubdomain, getSubdomainPaths } from "@/lib/db";

// Our types for the site data
export interface Props {
  name: String
  description: String
  subdomain: String
  customDomain: String
}

export default function Index(props: Props) {
  const router = useRouter()

  if (router.isFallback) {
    return (
      <>
        <p>
          Loading...
        </p>
      </>
    )
  }

  return (
    <>
      <h1>
        {props.name}
      </h1>
    </>
  )
}

// Getting the paths for all the subdomains in our database
export async function getStaticPaths() {
  const paths = await getSubdomainPaths()

  return {
    paths,
    fallback: true
  }
}

// Getting data to display on each custom subdomain
export async function getStaticProps({ params: { site } }) {
  const sites = await getHostnameDataBySubdomain(site)

  return {
    props: sites,
    revalidate: 3600
  }
}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

مرحله 5: افزودن میان افزار و داده های ساختگی

یک فایل میان‌افزار به ما این امکان را می‌دهد که تماس‌های api به باطن خود را رهگیری کنیم و کاری با آن انجام دهیم. در این مورد ما از میان افزار استفاده می کنیم تا مشخص کنیم که فراخوانی api برای چه نام میزبانی انجام می شود و داده های صحیح را نمایش می دهیم.

بیایید یک را ایجاد کنیم middleware.ts فایل در ریشه دایرکتوری پروژه ما

pages
└───api
└───sites
│      │
│    [site]
│      │  index.tsx
middleware.ts
package.json
etc
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

در داخل فایل Middleware.ts این کد را اضافه کنید:

import { NextRequest, NextResponse } from 'next/server'
import { getHostnameDataOrDefault } from './lib/db'

export const config = {
  matcher: ["https://dev.to/", "https://dev.to/about", '/_sites/:path'],
}

export default async function middleware(req: NextRequest) {
  const url = req.nextUrl

  // Get hostname (e.g. vercel.com, test.vercel.app, etc.)
  const hostname = req.headers.get('host')

  // If localhost, assign the host value manually
  // If prod, get the custom domain/subdomain value by removing the root URL
  // (in the case of "subdomain-3.localhost:3000", "localhost:3000" is the root URL)
  // process.env.NODE_ENV === "production" indicates that the app is deployed to a production environment
  // process.env.VERCEL === "1" indicates that the app is deployed on Vercel
  const currentHost =
    process.env.NODE_ENV === "production" && process.env.VERCEL === "1"
      ? hostname
        .replace(`.buildwithnext.com`, "")
      : hostname.replace(`.localhost:3000`, "");



  const data = await getHostnameDataOrDefault(currentHost)

  // Prevent security issues – users should not be able to canonically access
  // the pages/sites folder and its respective contents.
  if (url.pathname.startsWith(`/_sites`)) {
    url.pathname = `/404`
  } else {
    // console.log('URL 2', req.nextUrl.href)
    // rewrite to the current subdomain under the pages/sites folder
    url.pathname = `/_sites/${data.subdomain}${url.pathname}`
  }

  return NextResponse.rewrite(url)
}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

اینجا اتفاقات زیادی می افتد، اما من سعی کردم نظرات بیشتری اضافه کنم تا همه چیز روشن شود. ممکن است در ابتدا ترسناک به نظر برسد، اما سعی کنید خط به خط بروید و نظرات را بخوانید. در نهایت معنا پیدا خواهد کرد.

حالا بیایید جلو برویم و یک پوشه lib در ریشه دایرکتوری پروژه خود و یک فایل db.ts در داخل آن اضافه کنیم.

lib
└───db.ts
pages
└───api
└───sites
│      │
│    [site]
│      │  index.tsx
middleware.ts
package.json
etc
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

داخل فایل db.ts کد زیر را اضافه کنید:

// Dummy data to be replaced with your database
const hostnamesDB = [
  {
    name: 'This is Site 1',
    description: 'Subdomain + custom domain',
    subdomain: 'test1',
    customDomain: 'custom-domain-1.com',
    // Default subdomain for Preview deployments and for local development
    defaultForPreview: true,
  },
  {
    name: 'This is Site 2',
    description: 'Subdomain only',
    subdomain: 'test2',
  },
  {
    name: 'This is Site 3',
    description: 'Subdomain only',
    subdomain: 'test3',
  },
]
const DEFAULT_HOST = hostnamesDB.find((h) => h.defaultForPreview)

/**
 * Returns the data of the hostname based on its subdomain or custom domain
 * or the default host if there's no match.
 *
 * This method is used by middleware.ts
 */
export async function getHostnameDataOrDefault(
  subdomainOrCustomDomain?: string
) {
  if (!subdomainOrCustomDomain) return DEFAULT_HOST

  // check if site is a custom domain or a subdomain
  const customDomain = subdomainOrCustomDomain.includes('.')

  // fetch data from mock database using the site value as the key
  return (
    hostnamesDB.find((item) =>
      customDomain
        ? item.customDomain === subdomainOrCustomDomain
        : item.subdomain === subdomainOrCustomDomain
    ) ?? DEFAULT_HOST
  )
}

/**
 * Returns the data of the hostname based on its subdomain.
 *
 * This method is used by pages under middleware.ts
 */
export async function getHostnameDataBySubdomain(subdomain: string) {
  return hostnamesDB.find((item) => item.subdomain === subdomain)
}

/**
 * Returns the paths for `getStaticPaths` based on the subdomain of every
 * available hostname.
 */
export async function getSubdomainPaths() {
  // get all sites that have subdomains set up
  const subdomains = hostnamesDB.filter((item) => item.subdomain)

  // build paths for each of the sites in the previous two lists
  return subdomains.map((item) => {
    return { params: { site: item.subdomain } }
  })
}

export default hostnamesDB
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

این یکی کاملاً خود توضیحی است. ما اساساً برخی از داده های ساختگی داریم که از آنها برای دریافت زیر دامنه و داده ها برای نمایش در قسمت جلو استفاده می کنیم.

باز هم خط به خط بروید و نظرات را بخوانید.

مرحله 6: برنامه را تست و اجرا کنید

اکنون می توانیم اجرا کنیم npm run dev برای تست برنامه

به localhost:3000 بروید (مطمئن شوید که روی localhost:3000 اجرا می‌کنید زیرا این همان چیزی است که ما در فایل Middleware.ts قرار داده‌ایم).

حالا می توانید سعی کنید به test2.localhost:3000. محتوا باید به This is Site 2

اگر اکنون برنامه خود را مستقر کنید، باید به همین صورت عمل کند! vercel --prod

برنامه Next.js دارای معماری چند مستاجر است

مرحله 7: مراحل بعدی

اکنون می توانید داده های ساختگی را با یک پایگاه داده مانند PlanetScale جایگزین کنید و از Prisma ORM استفاده کنید تا کاربران خود زیر دامنه خود را راه اندازی کنند و داده ها را به سایت های خود اضافه کنند! من آموزش های بیشتری در مورد نحوه انجام این کار اضافه خواهم کرد، پس با ما همراه باشید.

امیدوارم این مقاله مفید بوده باشد!

نوشته های مشابه

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

دکمه بازگشت به بالا