first commit

This commit is contained in:
torbinus
2026-07-06 13:37:08 +02:00
commit 7e51514383
171 changed files with 29200 additions and 0 deletions
+57
View File
@@ -0,0 +1,57 @@
# Dependencies
node_modules
.pnp
.pnp.js
# Yarn 2+
.yarn/*
.yarn
!.yarn/patches
!.yarn/plugins
!.yarn/releases
!.yarn/sdks
!.yarn/versions
.pnp.*
# Testing
coverage
# Next.js
**/.next/
**/out/
# Production
**/build
**/dist
# Misc
*.pem
# Debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Local env files
**/.env
**/.env.local
**/.env.development.local
**/.env.test.local
**/.env.production.local
# Turbo
.turbo
.vscode
# vercel
.vercel
# macOS system files
.DS_Store
.DS_Store?
._*
.Spotlight-V100
.Trashes
ehthumbs.db
Thumbs.db
**/.DS_Store
+1
View File
@@ -0,0 +1 @@
auto-install-peers=true
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2022 Medusa
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+158
View File
@@ -0,0 +1,158 @@
<p align="center">
<a href="https://www.medusajs.com">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/59018053/229103275-b5e482bb-4601-46e6-8142-244f531cebdb.svg">
<source media="(prefers-color-scheme: light)" srcset="https://user-images.githubusercontent.com/59018053/229103726-e5b529a3-9b3f-4970-8a1f-c6af37f087bf.svg">
<img alt="Medusa logo" src="https://user-images.githubusercontent.com/59018053/229103726-e5b529a3-9b3f-4970-8a1f-c6af37f087bf.svg">
</picture>
</a>
</p>
<h1 align="center">
Medusa DTC Starter
</h1>
<h4 align="center">
<a href="https://docs.medusajs.com">Documentation</a> |
<a href="https://www.medusajs.com">Website</a>
</h4>
<p align="center">
Building blocks for digital commerce
</p>
<p align="center">
<a href="https://github.com/medusajs/medusa/blob/develop/LICENSE">
<img src="https://img.shields.io/badge/license-MIT-blue.svg" alt="Medusa is released under the MIT license." />
</a>
<a href="https://circleci.com/gh/medusajs/medusa">
<img src="https://circleci.com/gh/medusajs/medusa.svg?style=shield" alt="Current CircleCI build status." />
</a>
<a href="https://github.com/medusajs/medusa/blob/develop/CONTRIBUTING.md">
<img src="https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat" alt="PRs welcome!" />
</a>
<a href="https://www.producthunt.com/posts/medusa"><img src="https://img.shields.io/badge/Product%20Hunt-%231%20Product%20of%20the%20Day-%23DA552E" alt="Product Hunt"></a>
<a href="https://discord.gg/xpCwq3Kfn8">
<img src="https://img.shields.io/badge/chat-on%20discord-7289DA.svg" alt="Discord Chat" />
</a>
<a href="https://twitter.com/intent/follow?screen_name=medusajs">
<img src="https://img.shields.io/twitter/follow/medusajs.svg?label=Follow%20@medusajs" alt="Follow @medusajs" />
</a>
</p>
# Medusa DTC Starter
A production-ready monorepo starter for direct-to-consumer ecommerce stores powered by Medusa and Next.js. Includes a fully featured storefront with product browsing, cart, checkout, customer accounts, and order management.
## Features
- All of [Medusa's commerce features](https://docs.medusajs.com/resources/commerce-modules)
- Multi-region support with automatic country detection
- Product catalog with variant selection
- Cart with promotion codes
- Multi-step checkout with shipping and payment
- Customer accounts with order history and address management
- Order transfer between accounts
## Getting Started
### Deploy with Medusa Cloud
The fastest way to get started is deploying with [Medusa Cloud](https://cloud.medusajs.com):
1. [Create a Medusa Cloud account](https://cloud.medusajs.com)
2. Deploy this starter directly from your dashboard
### Local Installation
> **Prerequisites:
>
> - [Node.js](https://nodejs.org/) v20+
> - [PostgreSQL](https://www.postgresql.org/) v15+
> - [pnpm](https://pnpm.io/) v10+
1. Clone the repository and install dependencies:
```bash
git clone https://github.com/medusajs/dtc-starter.git
cd dtc-starter
pnpm install
```
2. Set up environment variables for the backend:
```bash
cp apps/backend/.env.template apps/backend/.env
```
3. Set the database URL in `apps/backend.env`:
```bash
# Replace with actual database URL, make sure the database exists.
DATABASE_URL=postgres://postgres:@localhost:5432/medusa-dtc-starter
```
4. Run migrations:
```bash
cd apps/backend
pnpm medusa db:migrate
```
5. Add admin user:
```bash
cd apps/backend
pnpm medusa user -e admin@test.com -p supersecret
```
6. Start Medusa backend:
```bash
cd apps/backend
pnpm dev
```
7. Open the admin dashboard at `localhost:9000/app` and log in. Retrieve your publishable API key at Settings > Publishable API key.
8. Set up environment variables for the storefront:
```bash
cp apps/storefront/.env.template apps/storefront/.env.local
```
9. Update `apps/storefront/.env.local` with your Medusa publishable API key:
```bash
NEXT_PUBLIC_MEDUSA_PUBLISHABLE_KEY=pk_6c3...
```
10. Start storefront:
```bash
cd apps/storefront
pnpm dev
```
The storefront runs on `http://localhost:8000`.
You can slo run the following command from the root to start both backend and storefront:
```bash
pnpm dev
```
## Configuration
The storefront is configured via environment variables in `apps/storefront/.env.local`:
| Variable | Description | Default |
|----------|-------------|---------|
| `NEXT_PUBLIC_MEDUSA_PUBLISHABLE_KEY` | Publishable API key from your Medusa backend | — |
| `NEXT_PUBLIC_MEDUSA_BACKEND_URL` | URL of your Medusa backend | `http://localhost:9000` |
| `NEXT_PUBLIC_DEFAULT_REGION` | Default region country code | `dk` |
| `NEXT_PUBLIC_BASE_URL` | Base URL of the storefront | `https://localhost:8000` |
| `NEXT_PUBLIC_STRIPE_KEY` | Stripe publishable key (optional) | — |
## Resources
- [Medusa Documentation](https://docs.medusajs.com)
- [Medusa Cloud](https://cloud.medusajs.com)
+8
View File
@@ -0,0 +1,8 @@
STORE_CORS=http://localhost:8000,https://docs.medusajs.com
ADMIN_CORS=http://localhost:5173,http://localhost:9000,https://docs.medusajs.com
AUTH_CORS=http://localhost:5173,http://localhost:9000,https://docs.medusajs.com
REDIS_URL=redis://localhost:6379
JWT_SECRET=supersecret
COOKIE_SECRET=supersecret
DATABASE_URL=
DB_NAME=medusa-backend
+26
View File
@@ -0,0 +1,26 @@
/dist
.env
.DS_Store
/uploads
/node_modules
yarn-error.log
.idea
coverage
!src/**
./tsconfig.tsbuildinfo
medusa-db.sql
build
.cache
.yarn/*
!.yarn/patches
!.yarn/plugins
!.yarn/releases
!.yarn/sdks
!.yarn/versions
.medusa
+62
View File
@@ -0,0 +1,62 @@
<p align="center">
<a href="https://www.medusajs.com">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/59018053/229103275-b5e482bb-4601-46e6-8142-244f531cebdb.svg">
<source media="(prefers-color-scheme: light)" srcset="https://user-images.githubusercontent.com/59018053/229103726-e5b529a3-9b3f-4970-8a1f-c6af37f087bf.svg">
<img alt="Medusa logo" src="https://user-images.githubusercontent.com/59018053/229103726-e5b529a3-9b3f-4970-8a1f-c6af37f087bf.svg">
</picture>
</a>
</p>
<h1 align="center">
Medusa
</h1>
<h4 align="center">
<a href="https://docs.medusajs.com">Documentation</a> |
<a href="https://www.medusajs.com">Website</a>
</h4>
<p align="center">
Building blocks for digital commerce
</p>
<p align="center">
<a href="https://github.com/medusajs/medusa/blob/master/CONTRIBUTING.md">
<img src="https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat" alt="PRs welcome!" />
</a>
<a href="https://www.producthunt.com/posts/medusa"><img src="https://img.shields.io/badge/Product%20Hunt-%231%20Product%20of%20the%20Day-%23DA552E" alt="Product Hunt"></a>
<a href="https://discord.gg/xpCwq3Kfn8">
<img src="https://img.shields.io/badge/chat-on%20discord-7289DA.svg" alt="Discord Chat" />
</a>
<a href="https://twitter.com/intent/follow?screen_name=medusajs">
<img src="https://img.shields.io/twitter/follow/medusajs.svg?label=Follow%20@medusajs" alt="Follow @medusajs" />
</a>
</p>
## Compatibility
This starter is compatible with versions >= 2 of `@medusajs/medusa`.
## Getting Started
Visit the [Quickstart Guide](https://docs.medusajs.com/learn/installation) to set up a server.
Visit the [Docs](https://docs.medusajs.com/learn/installation#get-started) to learn more about our system requirements.
## What is Medusa
Medusa is a set of commerce modules and tools that allow you to build rich, reliable, and performant commerce applications without reinventing core commerce logic. The modules can be customized and used to build advanced ecommerce stores, marketplaces, or any product that needs foundational commerce primitives. All modules are open-source and freely available on npm.
Learn more about [Medusas architecture](https://docs.medusajs.com/learn/introduction/architecture) and [commerce modules](https://docs.medusajs.com/learn/fundamentals/modules/commerce-modules) in the Docs.
## Community & Contributions
The community and core team are available in [GitHub Discussions](https://github.com/medusajs/medusa/discussions), where you can ask for support, discuss roadmap, and share ideas.
Join our [Discord server](https://discord.com/invite/medusajs) to meet other community members.
## Other channels
- [GitHub Issues](https://github.com/medusajs/medusa/issues)
- [Twitter](https://twitter.com/medusajs)
- [LinkedIn](https://www.linkedin.com/company/medusajs)
- [Medusa Blog](https://medusajs.com/blog/)
+4
View File
@@ -0,0 +1,4 @@
import { defineConfig } from "eslint/config"
import medusa from "@medusajs/eslint-plugin"
export default defineConfig([...medusa.configs.recommended])
+24
View File
@@ -0,0 +1,24 @@
// Uncomment this file to enable instrumentation and observability using OpenTelemetry
// Refer to the docs for installation instructions: https://docs.medusajs.com/learn/debugging-and-testing/instrumentation
// import { registerOtel } from "@medusajs/medusa"
// // If using an exporter other than Zipkin, require it here.
// import { ZipkinExporter } from "@opentelemetry/exporter-zipkin"
// // If using an exporter other than Zipkin, initialize it here.
// const exporter = new ZipkinExporter({
// serviceName: 'my-medusa-project',
// })
// export function register() {
// registerOtel({
// serviceName: 'medusajs',
// // pass exporter
// exporter,
// instrument: {
// http: true,
// workflows: true,
// query: true
// },
// })
// }
+27
View File
@@ -0,0 +1,27 @@
const { loadEnv } = require("@medusajs/utils");
loadEnv("test", process.cwd());
module.exports = {
transform: {
"^.+\\.[jt]s$": [
"@swc/jest",
{
jsc: {
parser: { syntax: "typescript", decorators: true },
},
},
],
},
testEnvironment: "node",
moduleFileExtensions: ["js", "ts", "json"],
modulePathIgnorePatterns: ["dist/", "<rootDir>/.medusa/"],
setupFiles: ["./integration-tests/setup.js"],
};
if (process.env.TEST_TYPE === "integration:http") {
module.exports.testMatch = ["**/integration-tests/http/*.spec.[jt]s"];
} else if (process.env.TEST_TYPE === "integration:modules") {
module.exports.testMatch = ["**/src/modules/*/__tests__/**/*.[jt]s"];
} else if (process.env.TEST_TYPE === "unit") {
module.exports.testMatch = ["**/src/**/__tests__/**/*.unit.spec.[jt]s"];
}
+60
View File
@@ -0,0 +1,60 @@
import { loadEnv, defineConfig } from '@medusajs/framework/utils'
loadEnv(process.env.NODE_ENV || 'development', process.cwd())
module.exports = defineConfig({
projectConfig: {
databaseUrl: process.env.DATABASE_URL,
redisUrl: process.env.REDIS_URL,
http: {
storeCors: process.env.STORE_CORS!,
adminCors: process.env.ADMIN_CORS!,
authCors: process.env.AUTH_CORS!,
jwtSecret: process.env.JWT_SECRET,
cookieSecret: process.env.COOKIE_SECRET,
}
},
plugins: [
{
resolve: "@easypayment/medusa-payment-paypal",
options: {},
},
],
modules: [
{
resolve: "@medusajs/medusa/file",
options: {
providers: [
{
resolve: "@medusajs/file-local",
id: "local",
options: {
backend_url: `${process.env.MEDUSA_BACKEND_URL}/static`,
},
},
],
},
},
{
resolve: "@medusajs/medusa/payment",
options: {
providers: [
{
// PayPal Smart Buttons (wallet checkout)
resolve: "@easypayment/medusa-payment-paypal/providers/paypal",
id: "paypal",
options: {},
dependencies: ["paypal_onboarding"],
},
{
// Advanced Card Fields (hosted card inputs)
resolve: "@easypayment/medusa-payment-paypal/providers/paypal_card",
id: "paypal_card",
options: {},
dependencies: ["paypal_onboarding"],
},
],
},
},
],
})
+61
View File
@@ -0,0 +1,61 @@
{
"name": "@dtc/backend",
"version": "0.0.1",
"description": "A starter for Medusa projects.",
"author": "Medusa (https://medusajs.com)",
"license": "MIT",
"keywords": [
"sqlite",
"postgres",
"typescript",
"ecommerce",
"headless",
"medusa"
],
"scripts": {
"build": "medusa build",
"start": "medusa start",
"dev": "medusa develop",
"lint": "medusa lint",
"test:integration:http": "TEST_TYPE=integration:http NODE_OPTIONS=--experimental-vm-modules jest --silent=false --runInBand --forceExit",
"test:integration:modules": "TEST_TYPE=integration:modules NODE_OPTIONS=--experimental-vm-modules jest --silent=false --runInBand --forceExit",
"test:unit": "TEST_TYPE=unit NODE_OPTIONS=--experimental-vm-modules jest --silent --runInBand --forceExit"
},
"dependencies": {
"@easypayment/medusa-payment-paypal": "^0.9.29",
"@medusajs/admin-sdk": "2.17.0",
"@medusajs/admin-shared": "2.17.0",
"@medusajs/caching": "2.17.0",
"@medusajs/cli": "2.17.0",
"@medusajs/dashboard": "2.17.0",
"@medusajs/draft-order": "2.17.0",
"@medusajs/framework": "2.17.0",
"@medusajs/medusa": "2.17.0",
"@medusajs/ui": "4.1.15",
"@tanstack/react-query": "5.64.2",
"react-i18next": "13.5.0",
"react-router-dom": "6.30.3",
"zod": "4.2.0"
},
"devDependencies": {
"@medusajs/test-utils": "2.17.0",
"@swc/core": "^1.7.28",
"@swc/jest": "^0.2.36",
"@types/jest": "^29.5.13",
"@types/node": "^20.12.11",
"@types/react": "^18.3.2",
"@types/react-dom": "^18.2.25",
"jest": "^29.7.0",
"prop-types": "^15.8.1",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"ts-node": "^10.9.2",
"typescript": "^5.6.2",
"vite": "^5.4.14",
"yalc": "^1.0.0-pre.53"
},
"engines": {
"node": ">=20"
},
"packageManager": "npm@10.9.2"
}
+33
View File
@@ -0,0 +1,33 @@
# Admin Customizations
You can extend the Medusa Admin to add widgets and new pages. Your customizations interact with API routes to provide merchants with custom functionalities.
> Learn more about Admin Extensions in [this documentation](https://docs.medusajs.com/learn/fundamentals/admin).
## Example: Create a Widget
A widget is a React component that can be injected into an existing page in the admin dashboard.
For example, create the file `src/admin/widgets/product-widget.tsx` with the following content:
```tsx title="src/admin/widgets/product-widget.tsx"
import { defineWidgetConfig } from "@medusajs/admin-sdk"
// The widget
const ProductWidget = () => {
return (
<div>
<h2>Product Widget</h2>
</div>
)
}
// The widget's configurations
export const config = defineWidgetConfig({
zone: "product.details.after",
})
export default ProductWidget
```
This inserts a widget with the text “Product Widget” at the end of a products details page.
@@ -0,0 +1,58 @@
# Admin Customizations Translations
The Medusa Admin dashboard supports multiple languages for its interface. Medusa uses [react-i18next](https://react.i18next.com/) to manage translations in the admin dashboard.
To add translations, create JSON translation files for each language under the `src/admin/i18n/json` directory. For example, create the `src/admin/i18n/json/en.json` file with the following content:
```json
{
"brands": {
"title": "Brands",
"description": "Manage your product brands"
},
"done": "Done"
}
```
Then, export the translations in `src/admin/i18n/index.ts`:
```ts
import en from "./json/en.json" with { type: "json" }
export default {
en: {
translation: en,
},
}
```
Finally, use translations in your admin widgets and routes using the `useTranslation` hook:
```tsx
import { defineWidgetConfig } from "@medusajs/admin-sdk"
import { Button, Container, Heading } from "@medusajs/ui"
import { useTranslation } from "react-i18next"
const ProductWidget = () => {
const { t } = useTranslation()
return (
<Container className="p-0">
<div className="flex items-center justify-between px-6 py-4">
<Heading level="h2">{t("brands.title")}</Heading>
<p>{t("brands.description")}</p>
</div>
<div className="flex justify-end px-6 py-4">
<Button variant="primary">{t("done")}</Button>
</div>
</Container>
)
}
export const config = defineWidgetConfig({
zone: "product.details.before",
})
export default ProductWidget
```
Learn more about translating admin extensions in the [Translate Admin Customizations](https://docs.medusajs.com/learn/fundamentals/admin/translations) documentation.
@@ -0,0 +1 @@
export default {}
@@ -0,0 +1,27 @@
{
"compilerOptions": {
"target": "ES2020",
"useDefineForClassFields": true,
"lib": ["ES2020", "DOM", "DOM.Iterable"],
"module": "ESNext",
"skipLibCheck": true,
/* Bundler mode */
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react-jsx",
/* Linting */
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true
},
"include": [
".",
"../../.medusa/types/plugin-augmentations.d.ts"
]
}
+1
View File
@@ -0,0 +1 @@
/// <reference types="vite/client" />
+135
View File
@@ -0,0 +1,135 @@
# Custom API Routes
An API Route is a REST API endpoint.
An API Route is created in a TypeScript or JavaScript file under the `/src/api` directory of your Medusa application. The files name must be `route.ts` or `route.js`.
> Learn more about API Routes in [this documentation](https://docs.medusajs.com/learn/fundamentals/api-routes)
For example, to create a `GET` API Route at `/store/hello-world`, create the file `src/api/store/hello-world/route.ts` with the following content:
```ts
import type { MedusaRequest, MedusaResponse } from "@medusajs/framework/http";
export async function GET(req: MedusaRequest, res: MedusaResponse) {
res.json({
message: "Hello world!",
});
}
```
## Supported HTTP methods
The file based routing supports the following HTTP methods:
- GET
- POST
- PUT
- PATCH
- DELETE
- OPTIONS
- HEAD
You can define a handler for each of these methods by exporting a function with the name of the method in the paths `route.ts` file.
For example:
```ts
import type { MedusaRequest, MedusaResponse } from "@medusajs/framework/http";
export async function GET(req: MedusaRequest, res: MedusaResponse) {
// Handle GET requests
}
export async function POST(req: MedusaRequest, res: MedusaResponse) {
// Handle POST requests
}
export async function PUT(req: MedusaRequest, res: MedusaResponse) {
// Handle PUT requests
}
```
## Parameters
To create an API route that accepts a path parameter, create a directory within the route's path whose name is of the format `[param]`.
For example, if you want to define a route that takes a `productId` parameter, you can do so by creating a file called `/api/products/[productId]/route.ts`:
```ts
import type {
MedusaRequest,
MedusaResponse,
} from "@medusajs/framework/http"
export async function GET(req: MedusaRequest, res: MedusaResponse) {
const { productId } = req.params;
res.json({
message: `You're looking for product ${productId}`
})
}
```
To create an API route that accepts multiple path parameters, create within the file's path multiple directories whose names are of the format `[param]`.
For example, if you want to define a route that takes both a `productId` and a `variantId` parameter, you can do so by creating a file called `/api/products/[productId]/variants/[variantId]/route.ts`.
## Using the container
The Medusa container is available on `req.scope`. Use it to access modules' main services and other registered resources:
```ts
import type {
MedusaRequest,
MedusaResponse,
} from "@medusajs/framework/http"
export const GET = async (
req: MedusaRequest,
res: MedusaResponse
) => {
const productModuleService = req.scope.resolve("product")
const [, count] = await productModuleService.listAndCount()
res.json({
count,
})
}
```
## Middleware
You can apply middleware to your routes by creating a file called `/api/middlewares.ts`. This file must export a configuration object with what middleware you want to apply to which routes.
For example, if you want to apply a custom middleware function to the `/store/custom` route, you can do so by adding the following to your `/api/middlewares.ts` file:
```ts
import { defineMiddlewares } from "@medusajs/framework/http"
import type {
MedusaRequest,
MedusaResponse,
MedusaNextFunction,
} from "@medusajs/framework/http";
async function logger(
req: MedusaRequest,
res: MedusaResponse,
next: MedusaNextFunction
) {
console.log("Request received");
next();
}
export default defineMiddlewares({
routes: [
{
matcher: "/store/custom",
middlewares: [logger],
},
],
})
```
The `matcher` property can be either a string or a regular expression. The `middlewares` property accepts an array of middleware functions.
@@ -0,0 +1,8 @@
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http";
export async function GET(
req: MedusaRequest,
res: MedusaResponse
) {
res.sendStatus(200);
}
@@ -0,0 +1,8 @@
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http";
export async function GET(
req: MedusaRequest,
res: MedusaResponse
) {
res.sendStatus(200);
}
+38
View File
@@ -0,0 +1,38 @@
# Custom scheduled jobs
A scheduled job is a function executed at a specified interval of time in the background of your Medusa application.
> Learn more about scheduled jobs in [this documentation](https://docs.medusajs.com/learn/fundamentals/scheduled-jobs).
A scheduled job is created in a TypeScript or JavaScript file under the `src/jobs` directory.
For example, create the file `src/jobs/hello-world.ts` with the following content:
```ts
import {
MedusaContainer
} from "@medusajs/framework/types";
export default async function myCustomJob(container: MedusaContainer) {
const productService = container.resolve("product")
const products = await productService.listAndCountProducts();
// Do something with the products
}
export const config = {
name: "daily-product-report",
schedule: "0 0 * * *", // Every day at midnight
};
```
A scheduled job file must export:
- The function to be executed whenever its time to run the scheduled job.
- A configuration object defining the job. It has three properties:
- `name`: a unique name for the job.
- `schedule`: a [cron expression](https://crontab.guru/).
- `numberOfExecutions`: an optional integer, specifying how many times the job will execute before being removed
The `handler` is a function that accepts one parameter, `container`, which is a `MedusaContainer` instance used to resolve services.
+26
View File
@@ -0,0 +1,26 @@
# Module Links
A module link forms an association between two data models of different modules, while maintaining module isolation.
> Learn more about links in [this documentation](https://docs.medusajs.com/learn/fundamentals/module-links)
For example:
```ts
import BlogModule from "../modules/blog"
import ProductModule from "@medusajs/medusa/product"
import { defineLink } from "@medusajs/framework/utils"
export default defineLink(
ProductModule.linkable.product,
BlogModule.linkable.post
)
```
This defines a link between the Product Module's `product` data model and the Blog Module (custom module)'s `post` data model.
Then, in the Medusa application, run the following command to sync the links to the database:
```bash
npx medusa db:migrate
```
@@ -0,0 +1,839 @@
import { MedusaContainer } from "@medusajs/framework";
import {
ContainerRegistrationKeys,
ModuleRegistrationName,
Modules,
ProductStatus,
} from "@medusajs/framework/utils";
import {
createApiKeysWorkflow,
createCollectionsWorkflow,
createInventoryLevelsWorkflow,
createProductCategoriesWorkflow,
createProductOptionsWorkflow,
createProductsWorkflow,
createRegionsWorkflow,
createSalesChannelsWorkflow,
createShippingOptionsWorkflow,
createShippingProfilesWorkflow,
createStockLocationsWorkflow,
createStoresWorkflow,
createTaxRegionsWorkflow,
linkSalesChannelsToApiKeyWorkflow,
linkSalesChannelsToStockLocationWorkflow,
} from "@medusajs/medusa/core-flows";
export default async function initial_data_seed({
container,
}: {
container: MedusaContainer;
}) {
const logger = container.resolve(ContainerRegistrationKeys.LOGGER);
const link = container.resolve(ContainerRegistrationKeys.LINK);
const query = container.resolve(ContainerRegistrationKeys.QUERY);
const fulfillmentModuleService = container.resolve(
ModuleRegistrationName.FULFILLMENT
);
const countries = ["gb", "de", "dk", "se", "fr", "es", "it"];
logger.info("Seeding store data...");
const {
result: [defaultSalesChannel],
} = await createSalesChannelsWorkflow(container).run({
input: {
salesChannelsData: [
{
name: "Default Sales Channel",
description: "Created by Medusa",
},
],
},
});
const {
result: [publishableApiKey],
} = await createApiKeysWorkflow(container).run({
input: {
api_keys: [
{
title: "Default Publishable API Key",
type: "publishable",
created_by: "",
},
],
},
});
await linkSalesChannelsToApiKeyWorkflow(container).run({
input: {
id: publishableApiKey.id,
add: [defaultSalesChannel.id],
},
});
const {
result: [store],
} = await createStoresWorkflow(container).run({
input: {
stores: [
{
name: "Default Store",
supported_currencies: [
{
currency_code: "eur",
is_default: true,
},
{
currency_code: "usd",
is_default: false,
},
],
default_sales_channel_id: defaultSalesChannel.id,
},
],
},
});
logger.info("Seeding region data...");
const { result: regionResult } = await createRegionsWorkflow(container).run({
input: {
regions: [
{
name: "Europe",
currency_code: "eur",
countries,
payment_providers: ["pp_system_default"],
},
],
},
});
const region = regionResult[0];
logger.info("Finished seeding regions.");
logger.info("Seeding tax regions...");
await createTaxRegionsWorkflow(container).run({
input: countries.map((country_code) => ({
country_code,
provider_id: "tp_system",
})),
});
logger.info("Finished seeding tax regions.");
logger.info("Seeding stock location data...");
const { result: stockLocationResult } = await createStockLocationsWorkflow(
container
).run({
input: {
locations: [
{
name: "European Warehouse",
address: {
city: "Copenhagen",
country_code: "DK",
address_1: "",
},
},
],
},
});
const stockLocation = stockLocationResult[0];
await link.create({
[Modules.STOCK_LOCATION]: {
stock_location_id: stockLocation.id,
},
[Modules.FULFILLMENT]: {
fulfillment_provider_id: "manual_manual",
},
});
logger.info("Seeding fulfillment data...");
// This is created by a migration script in core.
const { data: shippingProfileResult } = await query.graph({
entity: "shipping_profile",
fields: ["id"],
});
const shippingProfile = shippingProfileResult[0];
const fulfillmentSet = await fulfillmentModuleService.createFulfillmentSets({
name: "European Warehouse delivery",
type: "shipping",
service_zones: [
{
name: "Europe",
geo_zones: [
{
country_code: "gb",
type: "country",
},
{
country_code: "de",
type: "country",
},
{
country_code: "dk",
type: "country",
},
{
country_code: "se",
type: "country",
},
{
country_code: "fr",
type: "country",
},
{
country_code: "es",
type: "country",
},
{
country_code: "it",
type: "country",
},
],
},
],
});
await link.create({
[Modules.STOCK_LOCATION]: {
stock_location_id: stockLocation.id,
},
[Modules.FULFILLMENT]: {
fulfillment_set_id: fulfillmentSet.id,
},
});
await createShippingOptionsWorkflow(container).run({
input: [
{
name: "Standard Shipping",
price_type: "flat",
provider_id: "manual_manual",
service_zone_id: fulfillmentSet.service_zones[0].id,
shipping_profile_id: shippingProfile.id,
type: {
label: "Standard",
description: "Ship in 2-3 days.",
code: "standard",
},
prices: [
{
currency_code: "usd",
amount: 10,
},
{
currency_code: "eur",
amount: 10,
},
{
region_id: region.id,
amount: 10,
},
],
rules: [
{
attribute: "enabled_in_store",
value: "true",
operator: "eq",
},
{
attribute: "is_return",
value: "false",
operator: "eq",
},
],
},
{
name: "Express Shipping",
price_type: "flat",
provider_id: "manual_manual",
service_zone_id: fulfillmentSet.service_zones[0].id,
shipping_profile_id: shippingProfile.id,
type: {
label: "Express",
description: "Ship in 24 hours.",
code: "express",
},
prices: [
{
currency_code: "usd",
amount: 10,
},
{
currency_code: "eur",
amount: 10,
},
{
region_id: region.id,
amount: 10,
},
],
rules: [
{
attribute: "enabled_in_store",
value: "true",
operator: "eq",
},
{
attribute: "is_return",
value: "false",
operator: "eq",
},
],
},
],
});
logger.info("Finished seeding fulfillment data.");
await linkSalesChannelsToStockLocationWorkflow(container).run({
input: {
id: stockLocation.id,
add: [defaultSalesChannel.id],
},
});
logger.info("Finished seeding stock location data.");
logger.info("Seeding product data...");
const { result: categoryResult } = await createProductCategoriesWorkflow(
container
).run({
input: {
product_categories: [
{
name: "Shirts",
is_active: true,
},
{
name: "Sweatshirts",
is_active: true,
},
{
name: "Pants",
is_active: true,
},
{
name: "Merch",
is_active: true,
},
],
},
});
const { result: productOptionsResult } = await createProductOptionsWorkflow(
container
).run({
input: {
product_options: [
{
title: "Size",
values: ["S", "M", "L", "XL"],
},
{
title: "Color",
values: ["Black", "White"],
},
],
},
});
const sizeOption = productOptionsResult.find((o) => o.title === "Size")!;
const colorOption = productOptionsResult.find((o) => o.title === "Color")!;
await createProductsWorkflow(container).run({
input: {
products: [
{
title: "Medusa T-Shirt",
category_ids: [
categoryResult.find((cat) => cat.name === "Shirts")!.id,
],
description:
"Reimagine the feeling of a classic T-shirt. With our cotton T-shirts, everyday essentials no longer have to be ordinary.",
handle: "t-shirt",
weight: 400,
status: ProductStatus.PUBLISHED,
shipping_profile_id: shippingProfile.id,
images: [
{
url: "https://medusa-public-images.s3.eu-west-1.amazonaws.com/tee-black-front.png",
},
{
url: "https://medusa-public-images.s3.eu-west-1.amazonaws.com/tee-black-back.png",
},
{
url: "https://medusa-public-images.s3.eu-west-1.amazonaws.com/tee-white-front.png",
},
{
url: "https://medusa-public-images.s3.eu-west-1.amazonaws.com/tee-white-back.png",
},
],
options: [
{ id: sizeOption.id },
{ id: colorOption.id },
],
variants: [
{
title: "S / Black",
sku: "SHIRT-S-BLACK",
options: {
Size: "S",
Color: "Black",
},
prices: [
{
amount: 10,
currency_code: "eur",
},
{
amount: 15,
currency_code: "usd",
},
],
},
{
title: "S / White",
sku: "SHIRT-S-WHITE",
options: {
Size: "S",
Color: "White",
},
prices: [
{
amount: 10,
currency_code: "eur",
},
{
amount: 15,
currency_code: "usd",
},
],
},
{
title: "M / Black",
sku: "SHIRT-M-BLACK",
options: {
Size: "M",
Color: "Black",
},
prices: [
{
amount: 10,
currency_code: "eur",
},
{
amount: 15,
currency_code: "usd",
},
],
},
{
title: "M / White",
sku: "SHIRT-M-WHITE",
options: {
Size: "M",
Color: "White",
},
prices: [
{
amount: 10,
currency_code: "eur",
},
{
amount: 15,
currency_code: "usd",
},
],
},
{
title: "L / Black",
sku: "SHIRT-L-BLACK",
options: {
Size: "L",
Color: "Black",
},
prices: [
{
amount: 10,
currency_code: "eur",
},
{
amount: 15,
currency_code: "usd",
},
],
},
{
title: "L / White",
sku: "SHIRT-L-WHITE",
options: {
Size: "L",
Color: "White",
},
prices: [
{
amount: 10,
currency_code: "eur",
},
{
amount: 15,
currency_code: "usd",
},
],
},
{
title: "XL / Black",
sku: "SHIRT-XL-BLACK",
options: {
Size: "XL",
Color: "Black",
},
prices: [
{
amount: 10,
currency_code: "eur",
},
{
amount: 15,
currency_code: "usd",
},
],
},
{
title: "XL / White",
sku: "SHIRT-XL-WHITE",
options: {
Size: "XL",
Color: "White",
},
prices: [
{
amount: 10,
currency_code: "eur",
},
{
amount: 15,
currency_code: "usd",
},
],
},
],
sales_channels: [
{
id: defaultSalesChannel.id,
},
],
},
{
title: "Medusa Sweatshirt",
category_ids: [
categoryResult.find((cat) => cat.name === "Sweatshirts")!.id,
],
description:
"Reimagine the feeling of a classic sweatshirt. With our cotton sweatshirt, everyday essentials no longer have to be ordinary.",
handle: "sweatshirt",
weight: 400,
status: ProductStatus.PUBLISHED,
shipping_profile_id: shippingProfile.id,
images: [
{
url: "https://medusa-public-images.s3.eu-west-1.amazonaws.com/sweatshirt-vintage-front.png",
},
{
url: "https://medusa-public-images.s3.eu-west-1.amazonaws.com/sweatshirt-vintage-back.png",
},
],
options: [{ id: sizeOption.id }],
variants: [
{
title: "S",
sku: "SWEATSHIRT-S",
options: {
Size: "S",
},
prices: [
{
amount: 10,
currency_code: "eur",
},
{
amount: 15,
currency_code: "usd",
},
],
},
{
title: "M",
sku: "SWEATSHIRT-M",
options: {
Size: "M",
},
prices: [
{
amount: 10,
currency_code: "eur",
},
{
amount: 15,
currency_code: "usd",
},
],
},
{
title: "L",
sku: "SWEATSHIRT-L",
options: {
Size: "L",
},
prices: [
{
amount: 10,
currency_code: "eur",
},
{
amount: 15,
currency_code: "usd",
},
],
},
{
title: "XL",
sku: "SWEATSHIRT-XL",
options: {
Size: "XL",
},
prices: [
{
amount: 10,
currency_code: "eur",
},
{
amount: 15,
currency_code: "usd",
},
],
},
],
sales_channels: [
{
id: defaultSalesChannel.id,
},
],
},
{
title: "Medusa Sweatpants",
category_ids: [
categoryResult.find((cat) => cat.name === "Pants")!.id,
],
description:
"Reimagine the feeling of classic sweatpants. With our cotton sweatpants, everyday essentials no longer have to be ordinary.",
handle: "sweatpants",
weight: 400,
status: ProductStatus.PUBLISHED,
shipping_profile_id: shippingProfile.id,
images: [
{
url: "https://medusa-public-images.s3.eu-west-1.amazonaws.com/sweatpants-gray-front.png",
},
{
url: "https://medusa-public-images.s3.eu-west-1.amazonaws.com/sweatpants-gray-back.png",
},
],
options: [{ id: sizeOption.id }],
variants: [
{
title: "S",
sku: "SWEATPANTS-S",
options: {
Size: "S",
},
prices: [
{
amount: 10,
currency_code: "eur",
},
{
amount: 15,
currency_code: "usd",
},
],
},
{
title: "M",
sku: "SWEATPANTS-M",
options: {
Size: "M",
},
prices: [
{
amount: 10,
currency_code: "eur",
},
{
amount: 15,
currency_code: "usd",
},
],
},
{
title: "L",
sku: "SWEATPANTS-L",
options: {
Size: "L",
},
prices: [
{
amount: 10,
currency_code: "eur",
},
{
amount: 15,
currency_code: "usd",
},
],
},
{
title: "XL",
sku: "SWEATPANTS-XL",
options: {
Size: "XL",
},
prices: [
{
amount: 10,
currency_code: "eur",
},
{
amount: 15,
currency_code: "usd",
},
],
},
],
sales_channels: [
{
id: defaultSalesChannel.id,
},
],
},
{
title: "Medusa Shorts",
category_ids: [
categoryResult.find((cat) => cat.name === "Merch")!.id,
],
description:
"Reimagine the feeling of classic shorts. With our cotton shorts, everyday essentials no longer have to be ordinary.",
handle: "shorts",
weight: 400,
status: ProductStatus.PUBLISHED,
shipping_profile_id: shippingProfile.id,
images: [
{
url: "https://medusa-public-images.s3.eu-west-1.amazonaws.com/shorts-vintage-front.png",
},
{
url: "https://medusa-public-images.s3.eu-west-1.amazonaws.com/shorts-vintage-back.png",
},
],
options: [{ id: sizeOption.id }],
variants: [
{
title: "S",
sku: "SHORTS-S",
options: {
Size: "S",
},
prices: [
{
amount: 10,
currency_code: "eur",
},
{
amount: 15,
currency_code: "usd",
},
],
},
{
title: "M",
sku: "SHORTS-M",
options: {
Size: "M",
},
prices: [
{
amount: 10,
currency_code: "eur",
},
{
amount: 15,
currency_code: "usd",
},
],
},
{
title: "L",
sku: "SHORTS-L",
options: {
Size: "L",
},
prices: [
{
amount: 10,
currency_code: "eur",
},
{
amount: 15,
currency_code: "usd",
},
],
},
{
title: "XL",
sku: "SHORTS-XL",
options: {
Size: "XL",
},
prices: [
{
amount: 10,
currency_code: "eur",
},
{
amount: 15,
currency_code: "usd",
},
],
},
],
sales_channels: [
{
id: defaultSalesChannel.id,
},
],
},
],
},
});
logger.info("Finished seeding product data.");
logger.info("Seeding inventory levels.");
const { data: inventoryItems } = await query.graph({
entity: "inventory_item",
fields: ["id"],
});
await createInventoryLevelsWorkflow(container).run({
input: {
inventory_levels: inventoryItems.map((item) => ({
location_id: stockLocation.id,
stocked_quantity: 1000000,
inventory_item_id: item.id,
})),
},
});
logger.info("Finished seeding inventory levels data.");
}
+117
View File
@@ -0,0 +1,117 @@
# Custom Module
A module is a package of reusable functionalities. It can be integrated into your Medusa application without affecting the overall system. You can create a module as part of a plugin.
> Learn more about modules in [this documentation](https://docs.medusajs.com/learn/fundamentals/modules).
To create a module:
## 1. Create a Data Model
A data model represents a table in the database. You create a data model in a TypeScript or JavaScript file under the `models` directory of a module.
For example, create the file `src/modules/blog/models/post.ts` with the following content:
```ts
import { model } from "@medusajs/framework/utils"
const Post = model.define("post", {
id: model.id().primaryKey(),
title: model.text(),
})
export default Post
```
## 2. Create a Service
A module must define a service. A service is a TypeScript or JavaScript class holding methods related to a business logic or commerce functionality.
For example, create the file `src/modules/blog/service.ts` with the following content:
```ts
import { MedusaService } from "@medusajs/framework/utils"
import Post from "./models/post"
class BlogModuleService extends MedusaService({
Post,
}){
}
export default BlogModuleService
```
## 3. Export Module Definition
A module must have an `index.ts` file in its root directory that exports its definition. The definition specifies the main service of the module.
For example, create the file `src/modules/blog/index.ts` with the following content:
```ts
import BlogModuleService from "./service"
import { Module } from "@medusajs/framework/utils"
export const BLOG_MODULE = "blog"
export default Module(BLOG_MODULE, {
service: BlogModuleService,
})
```
## 4. Add Module to Medusa's Configurations
To start using the module, add it to `medusa-config.ts`:
```ts
module.exports = defineConfig({
projectConfig: {
// ...
},
modules: [
{
resolve: "./src/modules/blog",
},
],
})
```
## 5. Generate and Run Migrations
To generate migrations for your module, run the following command:
```bash
npx medusa db:generate blog
```
Then, to run migrations, run the following command:
```bash
npx medusa db:migrate
```
## Use Module
You can use the module in customizations within the Medusa application, such as workflows and API routes.
For example, to use the module in an API route:
```ts
import { MedusaRequest, MedusaResponse } from "@medusajs/framework"
import BlogModuleService from "../../../modules/blog/service"
import { BLOG_MODULE } from "../../../modules/blog"
export async function GET(
req: MedusaRequest,
res: MedusaResponse
): Promise<void> {
const blogModuleService: BlogModuleService = req.scope.resolve(
BLOG_MODULE
)
const posts = await blogModuleService.listPosts()
res.json({
posts
})
}
```
@@ -0,0 +1,61 @@
# Custom subscribers
Subscribers handle events emitted in the Medusa application.
> Learn more about Subscribers in [this documentation](https://docs.medusajs.com/learn/fundamentals/events-and-subscribers).
The subscriber is created in a TypeScript or JavaScript file under the `src/subscribers` directory.
For example, create the file `src/subscribers/product-created.ts` with the following content:
```ts
import {
type SubscriberConfig,
} from "@medusajs/framework"
// subscriber function
export default async function productCreateHandler() {
console.log("A product was created")
}
// subscriber config
export const config: SubscriberConfig = {
event: "product.created",
}
```
A subscriber file must export:
- The subscriber function that is an asynchronous function executed whenever the associated event is triggered.
- A configuration object defining the event this subscriber is listening to.
## Subscriber Parameters
A subscriber receives an object having the following properties:
- `event`: An object holding the event's details. It has a `data` property, which is the event's data payload.
- `container`: The Medusa container. Use it to resolve modules' main services and other registered resources.
```ts
import type {
SubscriberArgs,
SubscriberConfig,
} from "@medusajs/framework"
export default async function productCreateHandler({
event: { data },
container,
}: SubscriberArgs<{ id: string }>) {
const productId = data.id
const productModuleService = container.resolve("product")
const product = await productModuleService.retrieveProduct(productId)
console.log(`The product ${product.title} was created`)
}
export const config: SubscriberConfig = {
event: "product.created",
}
```
@@ -0,0 +1,81 @@
# Custom Workflows
A workflow is a series of queries and actions that complete a task.
The workflow is created in a TypeScript or JavaScript file under the `src/workflows` directory.
> Learn more about workflows in [this documentation](https://docs.medusajs.com/learn/fundamentals/workflows).
For example:
```ts
import {
createStep,
createWorkflow,
WorkflowResponse,
StepResponse,
} from "@medusajs/framework/workflows-sdk"
const step1 = createStep("step-1", async () => {
return new StepResponse(`Hello from step one!`)
})
type WorkflowInput = {
name: string
}
const step2 = createStep(
"step-2",
async ({ name }: WorkflowInput) => {
return new StepResponse(`Hello ${name} from step two!`)
}
)
type WorkflowOutput = {
message1: string
message2: string
}
const helloWorldWorkflow = createWorkflow(
"hello-world",
(input: WorkflowInput) => {
const greeting1 = step1()
const greeting2 = step2(input)
return new WorkflowResponse({
message1: greeting1,
message2: greeting2
})
}
)
export default helloWorldWorkflow
```
## Execute Workflow
You can execute the workflow from other resources, such as API routes, scheduled jobs, or subscribers.
For example, to execute the workflow in an API route:
```ts
import type {
MedusaRequest,
MedusaResponse,
} from "@medusajs/framework"
import myWorkflow from "../../../workflows/hello-world"
export async function GET(
req: MedusaRequest,
res: MedusaResponse
) {
const { result } = await myWorkflow(req.scope)
.run({
input: {
name: req.query.name as string,
},
})
res.send(result)
}
```
Binary file not shown.

After

Width:  |  Height:  |  Size: 930 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 930 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 237 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 409 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 286 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 113 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 184 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 125 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 501 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 276 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 279 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 248 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 211 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 103 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 256 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 236 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 235 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 253 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 261 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 241 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 357 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 309 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 199 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 849 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 469 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 350 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 192 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 212 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 377 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 332 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 223 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 996 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 720 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 155 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 741 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 930 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 325 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 268 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 200 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 318 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 340 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 211 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 708 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 378 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 233 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 312 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 731 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 547 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 349 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 231 KiB

+36
View File
@@ -0,0 +1,36 @@
{
"compilerOptions": {
"target": "ES2021",
"esModuleInterop": true,
"module": "Node16",
"moduleResolution": "Node16",
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"skipLibCheck": true,
"skipDefaultLibCheck": true,
"declaration": false,
"sourceMap": false,
"inlineSourceMap": true,
"outDir": "./.medusa/server",
"rootDir": "./",
"jsx": "react-jsx",
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"checkJs": false,
"strictNullChecks": true
},
"ts-node": {
"swc": true
},
"include": [
"**/*",
".medusa/types/*"
],
"exclude": [
"node_modules",
".medusa/server",
".medusa/admin",
".cache",
"eslint.config.*",
]
}
+4
View File
@@ -0,0 +1,4 @@
import { defineConfig } from "eslint/config"
import medusa from "@medusajs/eslint-plugin"
export default defineConfig([...medusa.configs.recommended])
+20385
View File
File diff suppressed because it is too large Load Diff
+38
View File
@@ -0,0 +1,38 @@
{
"name": "backend",
"private": false,
"packageManager": "npm@10.9.2",
"engines": {
"node": ">=20"
},
"scripts": {
"dev": "turbo dev",
"build": "turbo build",
"start": "turbo start",
"lint": "turbo lint",
"test": "turbo test",
"backend:seed": "turbo seed --filter=@dtc/backend",
"backend:dev": "turbo dev --filter=@dtc/backend",
"storefront:dev": "turbo dev --filter=@dtc/storefront"
},
"pnpm": {
"overrides": {
"@types/react": "19.0.5",
"@types/react-dom": "19.0.5"
}
},
"devDependencies": {
"@medusajs/eslint-plugin": "2.16.0",
"eslint": "^9.39.4",
"jiti": "^2.7.0",
"prettier": "^3.2.5",
"turbo": "^2.0.14"
},
"workspaces": [
"apps/**",
"!apps/backend/.medusa/**"
],
"overrides": {
"ajv": "^8.0.0"
}
}
+25
View File
@@ -0,0 +1,25 @@
{
"$schema": "https://turbo.build/schema.json",
"tasks": {
"build": {
"dependsOn": ["^build"],
"outputs": ["dist/**", ".next/**"]
},
"dev": {
"cache": false,
"persistent": true
},
"start": {
"dependsOn": ["^build"]
},
"lint": {
"outputs": []
},
"test": {
"outputs": []
},
"seed": {
"outputs": []
}
}
}