مستنداتبخش برنامه نویساناتصال موتور MedusaJS 2.0

معماری و یکپارچه‌سازی جامع موتور MedusaJS 2.0

موتور تجارت الکترونیک MedusaJS 2.0 (v2.15.5) تمامی عملیات مربوط به کاتالوگ محصولات، قیمت‌گذاری بر اساس منطقه (Regions)، سبد خرید، فرایند تسویه‌حساب (Checkout)، ماژول‌های لینک بین‌ماژولی (Module Links) و حساب کاربری مشتریان را مدیریت می‌کند.

نکته بسیار مهم در تنظیمات Publishable API Key
پس از ساخت کلید در پنل Medusa Admin، حتماً باید کلید ساخته شده را به **Sales Channel اصلی** متصل کنید؛ در غیر این صورت محصولات روی فرانت‌اند نمایش داده نخواهند شد.

معماری لینک‌های بین‌ماژولی Medusa v2 (Module Links)

در نسخه Medusa v2، ماژول‌ها کاملاً ایزوله هستند و ارتباط دیتابیسی بین مدل‌های ماژول‌های مستقل (مانند نظرات، سوالات، علاقه‌مندی‌ها و Strapi) از طریق defineLink در پوشه store-admin/src/links/ برقرار می‌شود:

product-strapi.tsنگاشت بین ID محصول مدوسا و شناسه‌های محتوایی Strapi CMS.
review-product.tsارتباط بین نظرات خریداران و محصول متناظر مدوسا.
wishlist-customer.tsارتباط بین لیست علاقه‌مندی‌ها و پروفایل کاربری مشتری.
question-product.tsاتصال پرسش و پاسخ‌های ثبت‌شده به محصولات کاتالوگ.
store-admin/src/links/review-product.ts
typescript
import { defineLink } from "@medusajs/framework/modules-sdk"
import ProductModule from "@medusajs/medusa/product"
import ProductReviewModule from "../modules/product-review"
export default defineLink(
ProductModule.linkable.product,
ProductReviewModule.linkable.productReview
)

اسکریپت‌های کاربردی مدیریت دیتابیس و سرور (Server Scripts)

در پوشه store-admin/src/scripts/ مجموعه اسکریپت‌های آماده‌ای جهت Seed و مدیریت محصولات تعبیه شده است:

اسکریپت Seed اولیه دیتابیس (منطقه‌ها، ارسال، محصولات)
bash
npm run seed
تزریق ۳۰ محصول نمونه کامل با متغیرها و قیمت‌ها
bash
npx medusa exec ./src/scripts/seed-30-products.ts
همگام‌سازی دستی و اجباری شاخص محصولات در Meilisearch
bash
npx medusa exec ./src/scripts/sync-meilisearch.ts
پاکسازی کامل محصولات برای شروع از صفر
bash
npx medusa exec ./src/scripts/delete-all-products.ts

لایه فراخوانی محصولات در فرانت‌اند (products.ts)

src/lib/data/products.ts
typescript
import { sdk } from "@lib/config"
import { HttpTypes } from "@medusajs/types"
import { getAuthHeaders, getCacheOptions } from "./cookies"
import { getLocaleHeader } from "@lib/util/get-locale-header"
import { getRegion, retrieveRegion } from "./regions"
import { isNetworkFetchError, warnMedusaUnreachable } from "@lib/util/medusa-fetch"
export const listProducts = async ({
pageParam = 1,
queryParams,
countryCode,
regionId,
disableAuth = false,
}: {
pageParam?: number
queryParams?: HttpTypes.FindParams & HttpTypes.StoreProductListParams
countryCode?: string
regionId?: string
disableAuth?: boolean
}): Promise<{
response: { products: HttpTypes.StoreProduct[]; count: number }
nextPage: number | null
queryParams?: HttpTypes.FindParams & HttpTypes.StoreProductListParams
}> => {
if (!countryCode && !regionId) {
throw new Error("Country code or region ID is required")
}
const limit = queryParams?.limit || 12
const _pageParam = Math.max(pageParam, 1)
const offset = _pageParam === 1 ? 0 : (_pageParam - 1) * limit
let region: HttpTypes.StoreRegion | undefined | null
if (countryCode) {
region = await getRegion(countryCode, disableAuth)
} else {
region = await retrieveRegion(regionId!, disableAuth)
}
if (!region) {
return { response: { products: [], count: 0 }, nextPage: null }
}
const headers = {
...(disableAuth ? {} : await getAuthHeaders()),
...(await getLocaleHeader(disableAuth)),
} as Record<string, string>
const next = {
...(disableAuth
? { tags: ["store-products"] }
: await getCacheOptions("store-products")),
revalidate: 3600,
}
try {
return await sdk.client
.fetch<{ products: HttpTypes.StoreProduct[]; count: number }>(
`/store/products`,
{
method: "GET",
query: {
limit,
offset,
region_id: region?.id,
fields:
"*variants.calculated_price,+variants.inventory_quantity,*variants.images,*options,*options.values,*variants.options,+metadata,+tags",
...queryParams,
},
headers,
next,
cache: "force-cache",
}
)
.then(({ products, count }) => {
const nextPage = count > offset + limit ? pageParam + 1 : null
return {
response: { products, count },
nextPage,
queryParams,
}
})
} catch (error) {
if (isNetworkFetchError(error)) {
warnMedusaUnreachable("listProducts")
return {
response: { products: [], count: 0 },
nextPage: null,
queryParams,
}
}
throw error
}
}

سرویس ارسال ایمیل و اعلان‌ها (Nodemailer SMTP & SendPulse)

سیستم ارسال ایمیل‌های اطلاع‌رسانی، فرم تماس و ثبت سفارش با استفاده از Nodemailer (SMTP) و SendPulse و قالب‌های واکنش‌گرا پیاده‌سازی شده است:

تنظیمات سرویس ایمیل در .env
env
SMTP_HOST=smtp.gmail.com
SMTP_PORT=587
SMTP_USER=your-email@gmail.com
SMTP_PASS=your-app-password
SMTP_FROM_ADDRESS=noreply@yourdomain.com
CONTACT_FORM_RECIPIENT=info@yourdomain.com
SENDPULSE_API_ID=your_sendpulse_id
SENDPULSE_API_SECRET=your_sendpulse_secret