chore(framework): Move and improve routes loader (#8392)
* chore(framework): Move and improve routes loader * cleanup * fix(framework): import
This commit is contained in:
1 parent
4081b3359d
commit
f81652bf6e
102 files changed
+921
-798
No files matched your search
@@ -41,8 +41,10 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@medusajs/types": "^1.11.16",
|
"@medusajs/types": "^1.11.16",
|
||||||
"@types/express": "^4.17.17",
|
"@types/express": "^4.17.17",
|
||||||
|
"@types/jsonwebtoken": "^8.5.9",
|
||||||
"cross-env": "^7.0.3",
|
"cross-env": "^7.0.3",
|
||||||
"rimraf": "^3.0.2",
|
"rimraf": "^3.0.2",
|
||||||
|
"supertest": "^4.0.2",
|
||||||
"tsc-alias": "^1.8.6",
|
"tsc-alias": "^1.8.6",
|
||||||
"typescript": "^5.1.6",
|
"typescript": "^5.1.6",
|
||||||
"vite": "^5.2.11"
|
"vite": "^5.2.11"
|
||||||
@@ -55,11 +57,14 @@
|
|||||||
"@medusajs/workflows-sdk": "^0.1.6",
|
"@medusajs/workflows-sdk": "^0.1.6",
|
||||||
"awilix": "^8.0.0",
|
"awilix": "^8.0.0",
|
||||||
"cookie-parser": "^1.4.6",
|
"cookie-parser": "^1.4.6",
|
||||||
|
"cors": "^2.8.5",
|
||||||
"express": "^4.18.2",
|
"express": "^4.18.2",
|
||||||
"express-session": "^1.17.3",
|
"express-session": "^1.17.3",
|
||||||
"ioredis": "^5.2.5",
|
"ioredis": "^5.2.5",
|
||||||
"ioredis-mock": "8.4.0",
|
"ioredis-mock": "8.4.0",
|
||||||
|
"jsonwebtoken": "^9.0.2",
|
||||||
"medusa-telemetry": "^0.0.17",
|
"medusa-telemetry": "^0.0.17",
|
||||||
"morgan": "^1.9.1"
|
"morgan": "^1.9.1",
|
||||||
|
"zod": "3.22.4"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
File renamed without changes.
File renamed without changes.
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
import { defineMiddlewares } from "../../../../../utils/define-middlewares"
|
import { defineMiddlewares } from "../../utils/define-middlewares"
|
||||||
|
|
||||||
export default defineMiddlewares({
|
export default defineMiddlewares({
|
||||||
errorHandler: (err, _req, res, _next) => {
|
errorHandler: (err, _req, res, _next) => {
|
||||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
+2
-2
@@ -1,10 +1,10 @@
|
|||||||
import { NextFunction, Request, Response, raw } from "express"
|
import { NextFunction, raw, Request, Response } from "express"
|
||||||
import {
|
import {
|
||||||
customersCreateMiddlewareMock,
|
customersCreateMiddlewareMock,
|
||||||
customersGlobalMiddlewareMock,
|
customersGlobalMiddlewareMock,
|
||||||
storeGlobalMiddlewareMock,
|
storeGlobalMiddlewareMock,
|
||||||
} from "../mocks"
|
} from "../mocks"
|
||||||
import { defineMiddlewares } from "../../../../../utils/define-middlewares"
|
import { defineMiddlewares } from "../../utils/define-middlewares"
|
||||||
|
|
||||||
const customersGlobalMiddleware = (
|
const customersGlobalMiddleware = (
|
||||||
req: Request,
|
req: Request,
|
||||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
+8
-23
@@ -3,24 +3,20 @@ import {
|
|||||||
ModulesDefinition,
|
ModulesDefinition,
|
||||||
registerMedusaModule,
|
registerMedusaModule,
|
||||||
} from "@medusajs/modules-sdk"
|
} from "@medusajs/modules-sdk"
|
||||||
import {
|
|
||||||
configManager,
|
|
||||||
ConfigModule,
|
|
||||||
container,
|
|
||||||
featureFlagsLoader,
|
|
||||||
logger,
|
|
||||||
} from "@medusajs/framework"
|
|
||||||
import { ContainerRegistrationKeys, generateJwtToken } from "@medusajs/utils"
|
import { ContainerRegistrationKeys, generateJwtToken } from "@medusajs/utils"
|
||||||
import { asValue } from "awilix"
|
import { asValue } from "awilix"
|
||||||
import express from "express"
|
import express from "express"
|
||||||
import querystring from "querystring"
|
import querystring from "querystring"
|
||||||
import supertest from "supertest"
|
import supertest from "supertest"
|
||||||
import apiLoader from "../../../../api"
|
|
||||||
import { getResolvedPlugins } from "../../../../helpers/resolve-plugins"
|
|
||||||
|
|
||||||
import RoutesLoader from "../.."
|
|
||||||
import { config } from "../mocks"
|
import { config } from "../mocks"
|
||||||
import { MedusaContainer } from "@medusajs/types"
|
import { MedusaContainer } from "@medusajs/types"
|
||||||
|
import { configManager, ConfigModule } from "../../../config"
|
||||||
|
import { container } from "../../../container"
|
||||||
|
import { featureFlagsLoader } from "../../../feature-flags"
|
||||||
|
import { logger } from "../../../logger"
|
||||||
|
import { MedusaRequest } from "../../types"
|
||||||
|
import { RoutesLoader } from "../../router"
|
||||||
|
|
||||||
function asArray(resolvers) {
|
function asArray(resolvers) {
|
||||||
return {
|
return {
|
||||||
@@ -90,30 +86,19 @@ export const createServer = async (rootDir) => {
|
|||||||
next()
|
next()
|
||||||
})
|
})
|
||||||
|
|
||||||
const plugins = getResolvedPlugins(rootDir, config) || []
|
|
||||||
|
|
||||||
await featureFlagsLoader()
|
await featureFlagsLoader()
|
||||||
await moduleLoader({ container, moduleResolutions, logger })
|
await moduleLoader({ container, moduleResolutions, logger })
|
||||||
|
|
||||||
app.use((req, res, next) => {
|
app.use((req, res, next) => {
|
||||||
req.scope = container.createScope() as MedusaContainer
|
;(req as MedusaRequest).scope = container.createScope() as MedusaContainer
|
||||||
next()
|
next()
|
||||||
})
|
})
|
||||||
|
|
||||||
// This where plugins normally load, but we simply load the routes
|
|
||||||
await new RoutesLoader({
|
await new RoutesLoader({
|
||||||
app,
|
app,
|
||||||
rootDir,
|
sourceDir: rootDir,
|
||||||
configModule: config,
|
|
||||||
}).load()
|
}).load()
|
||||||
|
|
||||||
// the apiLoader needs to be called after plugins otherwise the core middleware bleads into the plugins
|
|
||||||
await apiLoader({
|
|
||||||
container,
|
|
||||||
app: app,
|
|
||||||
plugins,
|
|
||||||
})
|
|
||||||
|
|
||||||
const superRequest = supertest(app)
|
const superRequest = supertest(app)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
+1
-3
@@ -1,7 +1,6 @@
|
|||||||
import express from "express"
|
import express from "express"
|
||||||
import { resolve } from "path"
|
import { resolve } from "path"
|
||||||
import {
|
import {
|
||||||
config,
|
|
||||||
customersCreateMiddlewareMock,
|
customersCreateMiddlewareMock,
|
||||||
customersGlobalMiddlewareMock,
|
customersGlobalMiddlewareMock,
|
||||||
storeGlobalMiddlewareMock,
|
storeGlobalMiddlewareMock,
|
||||||
@@ -230,8 +229,7 @@ describe("RoutesLoader", function () {
|
|||||||
)
|
)
|
||||||
const err = await new RoutesLoader({
|
const err = await new RoutesLoader({
|
||||||
app,
|
app,
|
||||||
rootDir,
|
sourceDir: rootDir,
|
||||||
configModule: config,
|
|
||||||
})
|
})
|
||||||
.load()
|
.load()
|
||||||
.catch((e) => e)
|
.catch((e) => e)
|
||||||
@@ -1 +1,5 @@
|
|||||||
export * from "./express-loader"
|
export * from "./express-loader"
|
||||||
|
export * from "./router"
|
||||||
|
export * from "./types"
|
||||||
|
export * from "./middlewares"
|
||||||
|
export * from "./utils/define-middlewares"
|
||||||
@@ -0,0 +1,208 @@
|
|||||||
|
import { ApiKeyDTO, IApiKeyModuleService } from "@medusajs/types"
|
||||||
|
import {
|
||||||
|
ContainerRegistrationKeys,
|
||||||
|
ModuleRegistrationName,
|
||||||
|
} from "@medusajs/utils"
|
||||||
|
import { NextFunction, RequestHandler } from "express"
|
||||||
|
import { JwtPayload, verify } from "jsonwebtoken"
|
||||||
|
import {
|
||||||
|
AuthContext,
|
||||||
|
AuthenticatedMedusaRequest,
|
||||||
|
MedusaRequest,
|
||||||
|
MedusaResponse,
|
||||||
|
} from "../types"
|
||||||
|
import { ConfigModule } from "../../config"
|
||||||
|
|
||||||
|
const SESSION_AUTH = "session"
|
||||||
|
const BEARER_AUTH = "bearer"
|
||||||
|
const API_KEY_AUTH = "api-key"
|
||||||
|
|
||||||
|
// This is the only hard-coded actor type, as API keys have special handling for now. We could also generalize API keys to carry the actor type with them.
|
||||||
|
const ADMIN_ACTOR_TYPE = "user"
|
||||||
|
|
||||||
|
type AuthType = typeof SESSION_AUTH | typeof BEARER_AUTH | typeof API_KEY_AUTH
|
||||||
|
|
||||||
|
type MedusaSession = {
|
||||||
|
auth_context: AuthContext
|
||||||
|
}
|
||||||
|
|
||||||
|
export const authenticate = (
|
||||||
|
actorType: string | string[],
|
||||||
|
authType: AuthType | AuthType[],
|
||||||
|
options: { allowUnauthenticated?: boolean; allowUnregistered?: boolean } = {}
|
||||||
|
): RequestHandler => {
|
||||||
|
const handler = async (
|
||||||
|
req: MedusaRequest,
|
||||||
|
res: MedusaResponse,
|
||||||
|
next: NextFunction
|
||||||
|
): Promise<void> => {
|
||||||
|
const authTypes = Array.isArray(authType) ? authType : [authType]
|
||||||
|
const actorTypes = Array.isArray(actorType) ? actorType : [actorType]
|
||||||
|
const req_ = req as AuthenticatedMedusaRequest
|
||||||
|
|
||||||
|
// We only allow authenticating using a secret API key on the admin
|
||||||
|
const isExclusivelyUser =
|
||||||
|
actorTypes.length === 1 && actorTypes[0] === ADMIN_ACTOR_TYPE
|
||||||
|
|
||||||
|
if (authTypes.includes(API_KEY_AUTH) && isExclusivelyUser) {
|
||||||
|
const apiKey = await getApiKeyInfo(req)
|
||||||
|
if (apiKey) {
|
||||||
|
req_.auth_context = {
|
||||||
|
actor_id: apiKey.id,
|
||||||
|
actor_type: "api-key",
|
||||||
|
auth_identity_id: "",
|
||||||
|
app_metadata: {},
|
||||||
|
}
|
||||||
|
|
||||||
|
return next()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// We try to extract the auth context either from the session or from a JWT token
|
||||||
|
let authContext: AuthContext | null = getAuthContextFromSession(
|
||||||
|
req.session,
|
||||||
|
authTypes,
|
||||||
|
actorTypes
|
||||||
|
)
|
||||||
|
|
||||||
|
if (!authContext) {
|
||||||
|
const {
|
||||||
|
projectConfig: { http },
|
||||||
|
} = req.scope.resolve<ConfigModule>(
|
||||||
|
ContainerRegistrationKeys.CONFIG_MODULE
|
||||||
|
)
|
||||||
|
|
||||||
|
authContext = getAuthContextFromJwtToken(
|
||||||
|
req.headers.authorization,
|
||||||
|
http.jwtSecret!,
|
||||||
|
authTypes,
|
||||||
|
actorTypes
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the entity is authenticated, and it is a registered actor we can continue
|
||||||
|
if (authContext?.actor_id) {
|
||||||
|
req_.auth_context = authContext
|
||||||
|
return next()
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the entity is authenticated, but there is no registered actor yet, we can continue (eg. in the case of a user invite) if allow unregistered is set
|
||||||
|
if (authContext?.auth_identity_id && options.allowUnregistered) {
|
||||||
|
req_.auth_context = authContext
|
||||||
|
return next()
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we allow unauthenticated requests (i.e public endpoints), just continue
|
||||||
|
if (options.allowUnauthenticated) {
|
||||||
|
return next()
|
||||||
|
}
|
||||||
|
|
||||||
|
res.status(401).json({ message: "Unauthorized" })
|
||||||
|
}
|
||||||
|
|
||||||
|
return handler as unknown as RequestHandler
|
||||||
|
}
|
||||||
|
|
||||||
|
const getApiKeyInfo = async (req: MedusaRequest): Promise<ApiKeyDTO | null> => {
|
||||||
|
const authHeader = req.headers.authorization
|
||||||
|
if (!authHeader) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
const [tokenType, token] = authHeader.split(" ")
|
||||||
|
if (tokenType.toLowerCase() !== "basic" || !token) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
// The token could have been base64 encoded, we want to decode it first.
|
||||||
|
let normalizedToken = token
|
||||||
|
if (!token.startsWith("sk_")) {
|
||||||
|
normalizedToken = Buffer.from(token, "base64").toString("utf-8")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Basic auth is defined as a username:password set, and since the token is set to the username we need to trim the colon
|
||||||
|
if (normalizedToken.endsWith(":")) {
|
||||||
|
normalizedToken = normalizedToken.slice(0, -1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Secret tokens start with 'sk_', and if it doesn't it could be a user JWT or a malformed token
|
||||||
|
if (!normalizedToken.startsWith("sk_")) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
const apiKeyModule = req.scope.resolve(
|
||||||
|
ModuleRegistrationName.API_KEY
|
||||||
|
) as IApiKeyModuleService
|
||||||
|
try {
|
||||||
|
const apiKey = await apiKeyModule.authenticate(normalizedToken)
|
||||||
|
if (!apiKey) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
return apiKey
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const getAuthContextFromSession = (
|
||||||
|
session: Partial<MedusaSession> = {},
|
||||||
|
authTypes: AuthType[],
|
||||||
|
actorTypes: string[]
|
||||||
|
): AuthContext | null => {
|
||||||
|
if (!authTypes.includes(SESSION_AUTH)) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
session.auth_context &&
|
||||||
|
(actorTypes.includes("*") ||
|
||||||
|
actorTypes.includes(session.auth_context.actor_type))
|
||||||
|
) {
|
||||||
|
return session.auth_context
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
const getAuthContextFromJwtToken = (
|
||||||
|
authHeader: string | undefined,
|
||||||
|
jwtSecret: string,
|
||||||
|
authTypes: AuthType[],
|
||||||
|
actorTypes: string[]
|
||||||
|
): AuthContext | null => {
|
||||||
|
if (!authTypes.includes(BEARER_AUTH)) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!authHeader) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
const re = /(\S+)\s+(\S+)/
|
||||||
|
const matches = authHeader.match(re)
|
||||||
|
|
||||||
|
// TODO: figure out how to obtain token (and store correct data in token)
|
||||||
|
if (matches) {
|
||||||
|
const tokenType = matches[1]
|
||||||
|
const token = matches[2]
|
||||||
|
if (tokenType.toLowerCase() === BEARER_AUTH) {
|
||||||
|
// get config jwt secret
|
||||||
|
// verify token and set authUser
|
||||||
|
try {
|
||||||
|
const verified = verify(token, jwtSecret) as JwtPayload
|
||||||
|
if (
|
||||||
|
actorTypes.includes("*") ||
|
||||||
|
actorTypes.includes(verified.actor_type)
|
||||||
|
) {
|
||||||
|
return verified as AuthContext
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
@@ -0,0 +1,102 @@
|
|||||||
|
import { NextFunction, Response } from "express"
|
||||||
|
|
||||||
|
import { ContainerRegistrationKeys, MedusaError } from "@medusajs/utils"
|
||||||
|
import { formatException } from "./exception-formatter"
|
||||||
|
import { MedusaRequest } from "../types"
|
||||||
|
import { logger as logger_ } from "../../logger"
|
||||||
|
|
||||||
|
const QUERY_RUNNER_RELEASED = "QueryRunnerAlreadyReleasedError"
|
||||||
|
const TRANSACTION_STARTED = "TransactionAlreadyStartedError"
|
||||||
|
const TRANSACTION_NOT_STARTED = "TransactionNotStartedError"
|
||||||
|
|
||||||
|
const API_ERROR = "api_error"
|
||||||
|
const INVALID_REQUEST_ERROR = "invalid_request_error"
|
||||||
|
const INVALID_STATE_ERROR = "invalid_state_error"
|
||||||
|
|
||||||
|
export function errorHandler() {
|
||||||
|
return (
|
||||||
|
err: MedusaError,
|
||||||
|
req: MedusaRequest,
|
||||||
|
res: Response,
|
||||||
|
next: NextFunction
|
||||||
|
) => {
|
||||||
|
const logger: typeof logger_ = req.scope.resolve(
|
||||||
|
ContainerRegistrationKeys.LOGGER
|
||||||
|
)
|
||||||
|
|
||||||
|
err = formatException(err)
|
||||||
|
|
||||||
|
logger.error(err)
|
||||||
|
|
||||||
|
const errorType = err.type || err.name
|
||||||
|
|
||||||
|
const errObj = {
|
||||||
|
code: err.code,
|
||||||
|
type: err.type,
|
||||||
|
message: err.message,
|
||||||
|
}
|
||||||
|
|
||||||
|
let statusCode = 500
|
||||||
|
switch (errorType) {
|
||||||
|
case QUERY_RUNNER_RELEASED:
|
||||||
|
case TRANSACTION_STARTED:
|
||||||
|
case TRANSACTION_NOT_STARTED:
|
||||||
|
case MedusaError.Types.CONFLICT:
|
||||||
|
statusCode = 409
|
||||||
|
errObj.code = INVALID_STATE_ERROR
|
||||||
|
errObj.message =
|
||||||
|
"The request conflicted with another request. You may retry the request with the provided Idempotency-Key."
|
||||||
|
break
|
||||||
|
case MedusaError.Types.UNAUTHORIZED:
|
||||||
|
statusCode = 401
|
||||||
|
break
|
||||||
|
case MedusaError.Types.PAYMENT_AUTHORIZATION_ERROR:
|
||||||
|
statusCode = 422
|
||||||
|
break
|
||||||
|
case MedusaError.Types.DUPLICATE_ERROR:
|
||||||
|
statusCode = 422
|
||||||
|
errObj.code = INVALID_REQUEST_ERROR
|
||||||
|
break
|
||||||
|
case MedusaError.Types.NOT_ALLOWED:
|
||||||
|
case MedusaError.Types.INVALID_DATA:
|
||||||
|
statusCode = 400
|
||||||
|
break
|
||||||
|
case MedusaError.Types.NOT_FOUND:
|
||||||
|
statusCode = 404
|
||||||
|
break
|
||||||
|
case MedusaError.Types.DB_ERROR:
|
||||||
|
statusCode = 500
|
||||||
|
errObj.code = API_ERROR
|
||||||
|
break
|
||||||
|
case MedusaError.Types.UNEXPECTED_STATE:
|
||||||
|
case MedusaError.Types.INVALID_ARGUMENT:
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
errObj.code = "unknown_error"
|
||||||
|
errObj.message = "An unknown error occurred."
|
||||||
|
errObj.type = "unknown_error"
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
res.status(statusCode).json(errObj)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @schema Error
|
||||||
|
* title: "Response Error"
|
||||||
|
* type: object
|
||||||
|
* properties:
|
||||||
|
* code:
|
||||||
|
* type: string
|
||||||
|
* description: A slug code to indicate the type of the error.
|
||||||
|
* enum: [invalid_state_error, invalid_request_error, api_error, unknown_error]
|
||||||
|
* message:
|
||||||
|
* type: string
|
||||||
|
* description: Description of the error that occurred.
|
||||||
|
* example: "first_name must be a string"
|
||||||
|
* type:
|
||||||
|
* type: string
|
||||||
|
* description: A slug indicating the type of the error.
|
||||||
|
* enum: [QueryRunnerAlreadyReleasedError, TransactionAlreadyStartedError, TransactionNotStartedError, conflict, unauthorized, payment_authorization_error, duplicate_error, not_allowed, invalid_data, not_found, database_error, unexpected_state, invalid_argument, unknown_error]
|
||||||
|
*/
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
import { MedusaError } from "@medusajs/utils"
|
||||||
|
|
||||||
|
export enum PostgresError {
|
||||||
|
DUPLICATE_ERROR = "23505",
|
||||||
|
FOREIGN_KEY_ERROR = "23503",
|
||||||
|
SERIALIZATION_FAILURE = "40001",
|
||||||
|
NULL_VIOLATION = "23502",
|
||||||
|
}
|
||||||
|
|
||||||
|
export const formatException = (err): MedusaError => {
|
||||||
|
switch (err.code) {
|
||||||
|
case PostgresError.DUPLICATE_ERROR:
|
||||||
|
return new MedusaError(
|
||||||
|
MedusaError.Types.DUPLICATE_ERROR,
|
||||||
|
`${err.table.charAt(0).toUpperCase()}${err.table.slice(
|
||||||
|
1
|
||||||
|
)} with ${err.detail.slice(4).replace(/[()=]/g, (s) => {
|
||||||
|
return s === "=" ? " " : ""
|
||||||
|
})}`
|
||||||
|
)
|
||||||
|
case PostgresError.FOREIGN_KEY_ERROR: {
|
||||||
|
const matches =
|
||||||
|
/Key \(([\w-\d]+)\)=\(([\w-\d]+)\) is not present in table "(\w+)"/g.exec(
|
||||||
|
err.detail
|
||||||
|
)
|
||||||
|
|
||||||
|
if (matches?.length !== 4) {
|
||||||
|
return new MedusaError(
|
||||||
|
MedusaError.Types.NOT_FOUND,
|
||||||
|
JSON.stringify(matches)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return new MedusaError(
|
||||||
|
MedusaError.Types.NOT_FOUND,
|
||||||
|
`${matches[3]?.charAt(0).toUpperCase()}${matches[3]?.slice(1)} with ${
|
||||||
|
matches[1]
|
||||||
|
} ${matches[2]} does not exist.`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
case PostgresError.SERIALIZATION_FAILURE: {
|
||||||
|
return new MedusaError(
|
||||||
|
MedusaError.Types.CONFLICT,
|
||||||
|
err?.detail ?? err?.message
|
||||||
|
)
|
||||||
|
}
|
||||||
|
case PostgresError.NULL_VIOLATION: {
|
||||||
|
return new MedusaError(
|
||||||
|
MedusaError.Types.INVALID_DATA,
|
||||||
|
`Can't insert null value in field ${err?.column} on insert in table ${err?.table}`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
export * from "./authenticate-middleware"
|
||||||
|
export * from "./error-handler"
|
||||||
|
export * from "./exception-formatter"
|
||||||
+196
-151
@@ -1,16 +1,13 @@
|
|||||||
import { ConfigModule } from "@medusajs/types"
|
|
||||||
import { parseCorsOrigins, promiseAll, wrapHandler } from "@medusajs/utils"
|
import { parseCorsOrigins, promiseAll, wrapHandler } from "@medusajs/utils"
|
||||||
import cors from "cors"
|
import cors from "cors"
|
||||||
import { type Express, json, Router, text, urlencoded } from "express"
|
import { type Express, json, Router, text, urlencoded } from "express"
|
||||||
import { readdir } from "fs/promises"
|
import { readdir } from "fs/promises"
|
||||||
import { extname, join, sep } from "path"
|
import { extname, join, parse, sep } from "path"
|
||||||
import { MedusaRequest, MedusaResponse } from "../../../types/routing"
|
|
||||||
import { authenticate, errorHandler } from "../../../utils/middlewares"
|
|
||||||
import { logger } from "@medusajs/framework"
|
|
||||||
import {
|
import {
|
||||||
AsyncRouteHandler,
|
|
||||||
GlobalMiddlewareDescriptor,
|
GlobalMiddlewareDescriptor,
|
||||||
HTTP_METHODS,
|
HTTP_METHODS,
|
||||||
|
MedusaRequest,
|
||||||
|
MedusaResponse,
|
||||||
MiddlewareRoute,
|
MiddlewareRoute,
|
||||||
MiddlewaresConfig,
|
MiddlewaresConfig,
|
||||||
MiddlewareVerb,
|
MiddlewareVerb,
|
||||||
@@ -19,6 +16,9 @@ import {
|
|||||||
RouteDescriptor,
|
RouteDescriptor,
|
||||||
RouteVerb,
|
RouteVerb,
|
||||||
} from "./types"
|
} from "./types"
|
||||||
|
import { authenticate, errorHandler } from "./middlewares"
|
||||||
|
import { configManager } from "../config"
|
||||||
|
import { logger } from "../logger"
|
||||||
|
|
||||||
const log = ({
|
const log = ({
|
||||||
activityId,
|
activityId,
|
||||||
@@ -163,40 +163,68 @@ function getBodyParserMiddleware(args?: ParserConfigArgs) {
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
export class RoutesLoader {
|
// TODO this router would need a proper rework, but it is out of scope right now
|
||||||
protected routesMap = new Map<string, RouteDescriptor>()
|
|
||||||
protected globalMiddlewaresDescriptor: GlobalMiddlewareDescriptor | undefined
|
|
||||||
|
|
||||||
protected app: Express
|
class ApiRoutesLoader {
|
||||||
protected router: Router
|
/**
|
||||||
protected activityId?: string
|
* Map of router path and its descriptor
|
||||||
protected rootDir: string
|
* @private
|
||||||
protected configModule: ConfigModule
|
*/
|
||||||
protected excludes: RegExp[] = [
|
#routesMap = new Map<string, RouteDescriptor>()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Global middleware descriptors
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
#globalMiddlewaresDescriptor: GlobalMiddlewareDescriptor | undefined
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An express instance
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
readonly #app: Express
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A router to assign the route to
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
readonly #router: Router
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An eventual activity id for information tracking
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
readonly #activityId?: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The list of file names to exclude from the routes scan
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
#excludes: RegExp[] = [
|
||||||
/\.DS_Store/,
|
/\.DS_Store/,
|
||||||
/(\.ts\.map|\.js\.map|\.d\.ts)/,
|
/(\.ts\.map|\.js\.map|\.d\.ts|\.md)/,
|
||||||
/^_[^/\\]*(\.[^/\\]+)?$/,
|
/^_[^/\\]*(\.[^/\\]+)?$/,
|
||||||
]
|
]
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Path from where to load the routes from
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
readonly #sourceDir: string
|
||||||
|
|
||||||
constructor({
|
constructor({
|
||||||
app,
|
app,
|
||||||
activityId,
|
activityId,
|
||||||
rootDir,
|
sourceDir,
|
||||||
configModule,
|
|
||||||
excludes = [],
|
|
||||||
}: {
|
}: {
|
||||||
app: Express
|
app: Express
|
||||||
activityId?: string
|
activityId?: string
|
||||||
rootDir: string
|
sourceDir: string
|
||||||
configModule: ConfigModule
|
|
||||||
excludes?: RegExp[]
|
|
||||||
}) {
|
}) {
|
||||||
this.app = app
|
this.#app = app
|
||||||
this.router = Router()
|
this.#router = Router()
|
||||||
this.activityId = activityId
|
this.#activityId = activityId
|
||||||
this.rootDir = rootDir
|
this.#sourceDir = sourceDir
|
||||||
this.configModule = configModule
|
|
||||||
this.excludes.push(...(excludes ?? []))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -215,7 +243,7 @@ export class RoutesLoader {
|
|||||||
}): void {
|
}): void {
|
||||||
if (!config?.routes && !config?.errorHandler) {
|
if (!config?.routes && !config?.errorHandler) {
|
||||||
log({
|
log({
|
||||||
activityId: this.activityId,
|
activityId: this.#activityId,
|
||||||
message: `Empty middleware config. Skipping middleware application.`,
|
message: `Empty middleware config. Skipping middleware application.`,
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -255,7 +283,7 @@ export class RoutesLoader {
|
|||||||
if (match?.[1] && !Number.isInteger(match?.[1])) {
|
if (match?.[1] && !Number.isInteger(match?.[1])) {
|
||||||
if (parameters.has(match?.[1])) {
|
if (parameters.has(match?.[1])) {
|
||||||
log({
|
log({
|
||||||
activityId: this.activityId,
|
activityId: this.#activityId,
|
||||||
message: `Duplicate parameters found in route ${route} (${match?.[1]})`,
|
message: `Duplicate parameters found in route ${route} (${match?.[1]})`,
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -289,12 +317,12 @@ export class RoutesLoader {
|
|||||||
*/
|
*/
|
||||||
protected async createRoutesConfig(): Promise<void> {
|
protected async createRoutesConfig(): Promise<void> {
|
||||||
await promiseAll(
|
await promiseAll(
|
||||||
[...this.routesMap.values()].map(async (descriptor: RouteDescriptor) => {
|
[...this.#routesMap.values()].map(async (descriptor: RouteDescriptor) => {
|
||||||
const absolutePath = descriptor.absolutePath
|
const absolutePath = descriptor.absolutePath
|
||||||
const route = descriptor.route
|
const route = descriptor.route
|
||||||
|
|
||||||
return await import(absolutePath).then((import_) => {
|
return await import(absolutePath).then((import_) => {
|
||||||
const map = this.routesMap
|
const map = this.#routesMap
|
||||||
|
|
||||||
const config: RouteConfig = {
|
const config: RouteConfig = {
|
||||||
routes: [],
|
routes: [],
|
||||||
@@ -353,7 +381,7 @@ export class RoutesLoader {
|
|||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
log({
|
log({
|
||||||
activityId: this.activityId,
|
activityId: this.#activityId,
|
||||||
message: `Skipping handler ${handler} in ${absolutePath}. Invalid HTTP method: ${handler}.`,
|
message: `Skipping handler ${handler} in ${absolutePath}. Invalid HTTP method: ${handler}.`,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -361,7 +389,7 @@ export class RoutesLoader {
|
|||||||
|
|
||||||
if (!config.routes?.length) {
|
if (!config.routes?.length) {
|
||||||
log({
|
log({
|
||||||
activityId: this.activityId,
|
activityId: this.#activityId,
|
||||||
message: `No valid route handlers detected in ${absolutePath}. Skipping route configuration.`,
|
message: `No valid route handlers detected in ${absolutePath}. Skipping route configuration.`,
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -376,29 +404,20 @@ export class RoutesLoader {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
protected createRoutesDescriptor({
|
protected createRoutesDescriptor(path: string) {
|
||||||
childPath,
|
|
||||||
parentPath,
|
|
||||||
}: {
|
|
||||||
childPath: string
|
|
||||||
parentPath?: string
|
|
||||||
}) {
|
|
||||||
const descriptor: RouteDescriptor = {
|
const descriptor: RouteDescriptor = {
|
||||||
absolutePath: childPath,
|
absolutePath: path,
|
||||||
relativePath: "",
|
relativePath: path,
|
||||||
route: "",
|
route: "",
|
||||||
priority: Infinity,
|
priority: Infinity,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (parentPath) {
|
const childPath = path.replace(this.#sourceDir, "")
|
||||||
childPath = childPath.replace(parentPath, "")
|
|
||||||
}
|
|
||||||
|
|
||||||
descriptor.relativePath = childPath
|
descriptor.relativePath = childPath
|
||||||
|
|
||||||
let routeToParse = childPath
|
let routeToParse = childPath
|
||||||
|
|
||||||
const pathSegments = childPath.split(sep)
|
const pathSegments = routeToParse.split(sep)
|
||||||
const lastSegment = pathSegments[pathSegments.length - 1]
|
const lastSegment = pathSegments[pathSegments.length - 1]
|
||||||
|
|
||||||
if (lastSegment.startsWith("route")) {
|
if (lastSegment.startsWith("route")) {
|
||||||
@@ -409,40 +428,36 @@ export class RoutesLoader {
|
|||||||
descriptor.route = this.parseRoute(routeToParse)
|
descriptor.route = this.parseRoute(routeToParse)
|
||||||
descriptor.priority = calculatePriority(descriptor.route)
|
descriptor.priority = calculatePriority(descriptor.route)
|
||||||
|
|
||||||
this.routesMap.set(childPath, descriptor)
|
this.#routesMap.set(path, descriptor)
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async createMiddlewaresDescriptor({
|
protected async createMiddlewaresDescriptor() {
|
||||||
dirPath,
|
const filePaths = await readdir(this.#sourceDir)
|
||||||
}: {
|
|
||||||
dirPath: string
|
|
||||||
}) {
|
|
||||||
const files = await readdir(dirPath)
|
|
||||||
|
|
||||||
const middlewareFilePath = files
|
const filteredFilePaths = filePaths.filter((path) => {
|
||||||
.filter((path) => {
|
const pathToCheck = path.replace(this.#sourceDir, "")
|
||||||
if (
|
return !pathToCheck
|
||||||
this.excludes.length &&
|
.split(sep)
|
||||||
this.excludes.some((exclude) => exclude.test(path))
|
.some((segment) =>
|
||||||
) {
|
this.#excludes.some((exclude) => exclude.test(segment))
|
||||||
return false
|
)
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
})
|
})
|
||||||
.find((file) => {
|
|
||||||
|
const middlewareFilePath = filteredFilePaths.find((file) => {
|
||||||
return file.replace(/\.[^/.]+$/, "") === MIDDLEWARES_NAME
|
return file.replace(/\.[^/.]+$/, "") === MIDDLEWARES_NAME
|
||||||
})
|
})
|
||||||
|
|
||||||
if (!middlewareFilePath) {
|
if (!middlewareFilePath) {
|
||||||
log({
|
log({
|
||||||
activityId: this.activityId,
|
activityId: this.#activityId,
|
||||||
message: `No middleware files found in ${dirPath}. Skipping middleware configuration.`,
|
message: `No middleware files found in ${
|
||||||
|
this.#sourceDir
|
||||||
|
}. Skipping middleware configuration.`,
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const absolutePath = join(dirPath, middlewareFilePath)
|
const absolutePath = join(this.#sourceDir, middlewareFilePath)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await import(absolutePath).then((import_) => {
|
await import(absolutePath).then((import_) => {
|
||||||
@@ -452,7 +467,7 @@ export class RoutesLoader {
|
|||||||
|
|
||||||
if (!middlewaresConfig) {
|
if (!middlewaresConfig) {
|
||||||
log({
|
log({
|
||||||
activityId: this.activityId,
|
activityId: this.#activityId,
|
||||||
message: `No middleware configuration found in ${absolutePath}. Skipping middleware configuration.`,
|
message: `No middleware configuration found in ${absolutePath}. Skipping middleware configuration.`,
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
@@ -471,11 +486,11 @@ export class RoutesLoader {
|
|||||||
|
|
||||||
this.validateMiddlewaresConfig(descriptor)
|
this.validateMiddlewaresConfig(descriptor)
|
||||||
|
|
||||||
this.globalMiddlewaresDescriptor = descriptor
|
this.#globalMiddlewaresDescriptor = descriptor
|
||||||
})
|
})
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
log({
|
log({
|
||||||
activityId: this.activityId,
|
activityId: this.#activityId,
|
||||||
message: `Failed to load middleware configuration in ${absolutePath}. Skipping middleware configuration.`,
|
message: `Failed to load middleware configuration in ${absolutePath}. Skipping middleware configuration.`,
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -483,54 +498,34 @@ export class RoutesLoader {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async createRoutesMap({
|
protected async createRoutesMap(): Promise<void> {
|
||||||
dirPath,
|
|
||||||
parentPath,
|
|
||||||
}: {
|
|
||||||
dirPath: string
|
|
||||||
parentPath?: string
|
|
||||||
}): Promise<void> {
|
|
||||||
await promiseAll(
|
await promiseAll(
|
||||||
await readdir(dirPath, { withFileTypes: true }).then((entries) => {
|
await readdir(this.#sourceDir, {
|
||||||
return entries
|
recursive: true,
|
||||||
.filter((entry) => {
|
withFileTypes: true,
|
||||||
if (
|
}).then((entries) => {
|
||||||
this.excludes.length &&
|
const fileEntries = entries.filter((entry) => {
|
||||||
this.excludes.some((exclude) => exclude.test(entry.name))
|
const fullPathFromSource = join(entry.path, entry.name).replace(
|
||||||
) {
|
this.#sourceDir,
|
||||||
return false
|
""
|
||||||
}
|
)
|
||||||
|
const isExcluded = fullPathFromSource
|
||||||
|
.split(sep)
|
||||||
|
.some((segment) =>
|
||||||
|
this.#excludes.some((exclude) => exclude.test(segment))
|
||||||
|
)
|
||||||
|
|
||||||
let name = entry.name
|
return (
|
||||||
|
!entry.isDirectory() &&
|
||||||
const extension = extname(name)
|
!isExcluded &&
|
||||||
|
parse(entry.name).name === ROUTE_NAME
|
||||||
if (extension) {
|
)
|
||||||
name = name.replace(extension, "")
|
|
||||||
}
|
|
||||||
|
|
||||||
if (entry.isFile() && name !== ROUTE_NAME) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
})
|
})
|
||||||
.map(async (entry) => {
|
|
||||||
const childPath = join(dirPath, entry.name)
|
|
||||||
|
|
||||||
if (entry.isDirectory()) {
|
return fileEntries.map(async (entry) => {
|
||||||
return await this.createRoutesMap({
|
const path = join(entry.path, entry.name)
|
||||||
dirPath: childPath,
|
return this.createRoutesDescriptor(path)
|
||||||
parentPath: parentPath ?? dirPath,
|
|
||||||
})
|
})
|
||||||
}
|
|
||||||
|
|
||||||
return this.createRoutesDescriptor({
|
|
||||||
childPath,
|
|
||||||
parentPath,
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.flat(Infinity)
|
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -539,7 +534,7 @@ export class RoutesLoader {
|
|||||||
* Apply the most specific body parser middleware to the router
|
* Apply the most specific body parser middleware to the router
|
||||||
*/
|
*/
|
||||||
applyBodyParserMiddleware(path: string, method: RouteVerb): void {
|
applyBodyParserMiddleware(path: string, method: RouteVerb): void {
|
||||||
const middlewareDescriptor = this.globalMiddlewaresDescriptor
|
const middlewareDescriptor = this.#globalMiddlewaresDescriptor
|
||||||
|
|
||||||
const mostSpecificConfig = findMatch(
|
const mostSpecificConfig = findMatch(
|
||||||
path,
|
path,
|
||||||
@@ -548,13 +543,13 @@ export class RoutesLoader {
|
|||||||
)
|
)
|
||||||
|
|
||||||
if (!mostSpecificConfig || mostSpecificConfig?.bodyParser === undefined) {
|
if (!mostSpecificConfig || mostSpecificConfig?.bodyParser === undefined) {
|
||||||
this.router[method.toLowerCase()](path, ...getBodyParserMiddleware())
|
this.#router[method.toLowerCase()](path, ...getBodyParserMiddleware())
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mostSpecificConfig?.bodyParser) {
|
if (mostSpecificConfig?.bodyParser) {
|
||||||
this.router[method.toLowerCase()](
|
this.#router[method.toLowerCase()](
|
||||||
path,
|
path,
|
||||||
...getBodyParserMiddleware(mostSpecificConfig?.bodyParser)
|
...getBodyParserMiddleware(mostSpecificConfig?.bodyParser)
|
||||||
)
|
)
|
||||||
@@ -572,7 +567,7 @@ export class RoutesLoader {
|
|||||||
* that they are applied before any other middleware.
|
* that they are applied before any other middleware.
|
||||||
*/
|
*/
|
||||||
applyRouteSpecificMiddlewares(): void {
|
applyRouteSpecificMiddlewares(): void {
|
||||||
const prioritizedRoutes = prioritize([...this.routesMap.values()])
|
const prioritizedRoutes = prioritize([...this.#routesMap.values()])
|
||||||
|
|
||||||
for (const descriptor of prioritizedRoutes) {
|
for (const descriptor of prioritizedRoutes) {
|
||||||
if (!descriptor.config?.routes?.length) {
|
if (!descriptor.config?.routes?.length) {
|
||||||
@@ -591,11 +586,11 @@ export class RoutesLoader {
|
|||||||
/**
|
/**
|
||||||
* Apply the admin cors
|
* Apply the admin cors
|
||||||
*/
|
*/
|
||||||
this.router.use(
|
this.#router.use(
|
||||||
descriptor.route,
|
descriptor.route,
|
||||||
cors({
|
cors({
|
||||||
origin: parseCorsOrigins(
|
origin: parseCorsOrigins(
|
||||||
this.configModule.projectConfig.http.adminCors
|
configManager.config.projectConfig.http.adminCors
|
||||||
),
|
),
|
||||||
credentials: true,
|
credentials: true,
|
||||||
})
|
})
|
||||||
@@ -606,11 +601,11 @@ export class RoutesLoader {
|
|||||||
/**
|
/**
|
||||||
* Apply the auth cors
|
* Apply the auth cors
|
||||||
*/
|
*/
|
||||||
this.router.use(
|
this.#router.use(
|
||||||
descriptor.route,
|
descriptor.route,
|
||||||
cors({
|
cors({
|
||||||
origin: parseCorsOrigins(
|
origin: parseCorsOrigins(
|
||||||
this.configModule.projectConfig.http.authCors
|
configManager.config.projectConfig.http.authCors
|
||||||
),
|
),
|
||||||
credentials: true,
|
credentials: true,
|
||||||
})
|
})
|
||||||
@@ -621,11 +616,11 @@ export class RoutesLoader {
|
|||||||
/**
|
/**
|
||||||
* Apply the store cors
|
* Apply the store cors
|
||||||
*/
|
*/
|
||||||
this.router.use(
|
this.#router.use(
|
||||||
descriptor.route,
|
descriptor.route,
|
||||||
cors({
|
cors({
|
||||||
origin: parseCorsOrigins(
|
origin: parseCorsOrigins(
|
||||||
this.configModule.projectConfig.http.storeCors
|
configManager.config.projectConfig.http.storeCors
|
||||||
),
|
),
|
||||||
credentials: true,
|
credentials: true,
|
||||||
})
|
})
|
||||||
@@ -634,7 +629,7 @@ export class RoutesLoader {
|
|||||||
|
|
||||||
// We only apply the auth middleware to store routes to populate the auth context. For actual authentication, users can just reapply the middleware.
|
// We only apply the auth middleware to store routes to populate the auth context. For actual authentication, users can just reapply the middleware.
|
||||||
if (!config.optedOutOfAuth && config.routeType === "store") {
|
if (!config.optedOutOfAuth && config.routeType === "store") {
|
||||||
this.router.use(
|
this.#router.use(
|
||||||
descriptor.route,
|
descriptor.route,
|
||||||
authenticate("customer", ["bearer", "session"], {
|
authenticate("customer", ["bearer", "session"], {
|
||||||
allowUnauthenticated: true,
|
allowUnauthenticated: true,
|
||||||
@@ -644,7 +639,7 @@ export class RoutesLoader {
|
|||||||
|
|
||||||
if (!config.optedOutOfAuth && config.routeType === "admin") {
|
if (!config.optedOutOfAuth && config.routeType === "admin") {
|
||||||
// We probably don't want to allow access to all endpoints using an api key, but it will do until we revamp our routing.
|
// We probably don't want to allow access to all endpoints using an api key, but it will do until we revamp our routing.
|
||||||
this.router.use(
|
this.#router.use(
|
||||||
descriptor.route,
|
descriptor.route,
|
||||||
authenticate("user", ["bearer", "session", "api-key"])
|
authenticate("user", ["bearer", "session", "api-key"])
|
||||||
)
|
)
|
||||||
@@ -664,7 +659,7 @@ export class RoutesLoader {
|
|||||||
* Apply the error handler middleware to the router
|
* Apply the error handler middleware to the router
|
||||||
*/
|
*/
|
||||||
applyErrorHandlerMiddleware(): void {
|
applyErrorHandlerMiddleware(): void {
|
||||||
const middlewareDescriptor = this.globalMiddlewaresDescriptor
|
const middlewareDescriptor = this.#globalMiddlewaresDescriptor
|
||||||
const errorHandlerFn = middlewareDescriptor?.config?.errorHandler
|
const errorHandlerFn = middlewareDescriptor?.config?.errorHandler
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -678,7 +673,7 @@ export class RoutesLoader {
|
|||||||
* If the user has provided a custom error handler then use it
|
* If the user has provided a custom error handler then use it
|
||||||
*/
|
*/
|
||||||
if (errorHandlerFn) {
|
if (errorHandlerFn) {
|
||||||
this.router.use(errorHandlerFn)
|
this.#router.use(errorHandlerFn as any)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -686,17 +681,17 @@ export class RoutesLoader {
|
|||||||
* If the user has not provided a custom error handler then use the
|
* If the user has not provided a custom error handler then use the
|
||||||
* default one.
|
* default one.
|
||||||
*/
|
*/
|
||||||
this.router.use(errorHandler())
|
this.#router.use(errorHandler() as any)
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async registerRoutes(): Promise<void> {
|
protected async registerRoutes(): Promise<void> {
|
||||||
const middlewareDescriptor = this.globalMiddlewaresDescriptor
|
const middlewareDescriptor = this.#globalMiddlewaresDescriptor
|
||||||
|
|
||||||
const shouldWrapHandler = middlewareDescriptor?.config
|
const shouldWrapHandler = middlewareDescriptor?.config
|
||||||
? middlewareDescriptor.config.errorHandler !== false
|
? middlewareDescriptor.config.errorHandler !== false
|
||||||
: true
|
: true
|
||||||
|
|
||||||
const prioritizedRoutes = prioritize([...this.routesMap.values()])
|
const prioritizedRoutes = prioritize([...this.#routesMap.values()])
|
||||||
|
|
||||||
for (const descriptor of prioritizedRoutes) {
|
for (const descriptor of prioritizedRoutes) {
|
||||||
if (!descriptor.config?.routes?.length) {
|
if (!descriptor.config?.routes?.length) {
|
||||||
@@ -707,7 +702,7 @@ export class RoutesLoader {
|
|||||||
|
|
||||||
for (const route of routes) {
|
for (const route of routes) {
|
||||||
log({
|
log({
|
||||||
activityId: this.activityId,
|
activityId: this.#activityId,
|
||||||
message: `Registering route [${route.method?.toUpperCase()}] - ${
|
message: `Registering route [${route.method?.toUpperCase()}] - ${
|
||||||
descriptor.route
|
descriptor.route
|
||||||
}`,
|
}`,
|
||||||
@@ -718,16 +713,16 @@ export class RoutesLoader {
|
|||||||
* we wrap the handler in a try/catch block.
|
* we wrap the handler in a try/catch block.
|
||||||
*/
|
*/
|
||||||
const handler = shouldWrapHandler
|
const handler = shouldWrapHandler
|
||||||
? wrapHandler(route.handler as AsyncRouteHandler)
|
? wrapHandler(route.handler as Parameters<typeof wrapHandler>[0])
|
||||||
: route.handler
|
: route.handler
|
||||||
|
|
||||||
this.router[route.method!.toLowerCase()](descriptor.route, handler)
|
this.#router[route.method!.toLowerCase()](descriptor.route, handler)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async registerMiddlewares(): Promise<void> {
|
protected async registerMiddlewares(): Promise<void> {
|
||||||
const descriptor = this.globalMiddlewaresDescriptor
|
const descriptor = this.#globalMiddlewaresDescriptor
|
||||||
|
|
||||||
if (!descriptor) {
|
if (!descriptor) {
|
||||||
return
|
return
|
||||||
@@ -757,17 +752,17 @@ export class RoutesLoader {
|
|||||||
|
|
||||||
for (const method of methods) {
|
for (const method of methods) {
|
||||||
log({
|
log({
|
||||||
activityId: this.activityId,
|
activityId: this.#activityId,
|
||||||
message: `Registering middleware [${method}] - ${route.matcher}`,
|
message: `Registering middleware [${method}] - ${route.matcher}`,
|
||||||
})
|
})
|
||||||
|
|
||||||
this.router[method.toLowerCase()](route.matcher, ...route.middlewares)
|
this.#router[method.toLowerCase()](route.matcher, ...route.middlewares)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async load() {
|
async load() {
|
||||||
performance && performance.mark("file-base-routing-start" + this.rootDir)
|
performance && performance.mark("file-base-routing-start" + this.#sourceDir)
|
||||||
|
|
||||||
let apiExists = true
|
let apiExists = true
|
||||||
|
|
||||||
@@ -778,15 +773,15 @@ export class RoutesLoader {
|
|||||||
* directory does not exist.
|
* directory does not exist.
|
||||||
*/
|
*/
|
||||||
try {
|
try {
|
||||||
await readdir(this.rootDir)
|
await readdir(this.#sourceDir)
|
||||||
} catch (_error) {
|
} catch (_error) {
|
||||||
apiExists = false
|
apiExists = false
|
||||||
}
|
}
|
||||||
|
|
||||||
if (apiExists) {
|
if (apiExists) {
|
||||||
await this.createMiddlewaresDescriptor({ dirPath: this.rootDir })
|
await this.createMiddlewaresDescriptor()
|
||||||
|
|
||||||
await this.createRoutesMap({ dirPath: this.rootDir })
|
await this.createRoutesMap()
|
||||||
await this.createRoutesConfig()
|
await this.createRoutesConfig()
|
||||||
|
|
||||||
this.applyRouteSpecificMiddlewares()
|
this.applyRouteSpecificMiddlewares()
|
||||||
@@ -802,28 +797,78 @@ export class RoutesLoader {
|
|||||||
* This prevents middleware from a plugin from
|
* This prevents middleware from a plugin from
|
||||||
* bleeding into the global middleware stack.
|
* bleeding into the global middleware stack.
|
||||||
*/
|
*/
|
||||||
this.app.use("/", this.router)
|
this.#app.use("/", this.#router)
|
||||||
}
|
}
|
||||||
|
|
||||||
performance && performance.mark("file-base-routing-end" + this.rootDir)
|
performance && performance.mark("file-base-routing-end" + this.#sourceDir)
|
||||||
const timeSpent =
|
const timeSpent =
|
||||||
performance &&
|
performance &&
|
||||||
performance
|
performance
|
||||||
.measure(
|
.measure(
|
||||||
"file-base-routing-measure" + this.rootDir,
|
"file-base-routing-measure" + this.#sourceDir,
|
||||||
"file-base-routing-start" + this.rootDir,
|
"file-base-routing-start" + this.#sourceDir,
|
||||||
"file-base-routing-end" + this.rootDir
|
"file-base-routing-end" + this.#sourceDir
|
||||||
)
|
)
|
||||||
?.duration?.toFixed(2)
|
?.duration?.toFixed(2)
|
||||||
|
|
||||||
log({
|
log({
|
||||||
activityId: this.activityId,
|
activityId: this.#activityId,
|
||||||
message: `Routes loaded in ${timeSpent} ms`,
|
message: `Routes loaded in ${timeSpent} ms`,
|
||||||
})
|
})
|
||||||
|
|
||||||
this.routesMap.clear()
|
this.#routesMap.clear()
|
||||||
this.globalMiddlewaresDescriptor = undefined
|
this.#globalMiddlewaresDescriptor = undefined
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default RoutesLoader
|
export class RoutesLoader {
|
||||||
|
/**
|
||||||
|
* An express instance
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
readonly #app: Express
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An eventual activity id for information tracking
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
readonly #activityId?: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Path from where to load the routes from
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
readonly #sourceDir: string | string[]
|
||||||
|
|
||||||
|
constructor({
|
||||||
|
app,
|
||||||
|
activityId,
|
||||||
|
sourceDir,
|
||||||
|
}: {
|
||||||
|
app: Express
|
||||||
|
activityId?: string
|
||||||
|
sourceDir: string | string[]
|
||||||
|
}) {
|
||||||
|
this.#app = app
|
||||||
|
this.#activityId = activityId
|
||||||
|
this.#sourceDir = sourceDir
|
||||||
|
}
|
||||||
|
|
||||||
|
async load() {
|
||||||
|
const normalizedSourcePath = Array.isArray(this.#sourceDir)
|
||||||
|
? this.#sourceDir
|
||||||
|
: [this.#sourceDir]
|
||||||
|
|
||||||
|
const promises = normalizedSourcePath.map(async (sourcePath) => {
|
||||||
|
const apiRoutesLoader = new ApiRoutesLoader({
|
||||||
|
app: this.#app,
|
||||||
|
activityId: this.#activityId,
|
||||||
|
sourceDir: sourcePath,
|
||||||
|
})
|
||||||
|
|
||||||
|
await apiRoutesLoader.load()
|
||||||
|
})
|
||||||
|
|
||||||
|
await promiseAll(promises)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,180 @@
|
|||||||
|
import { ZodObject } from "zod"
|
||||||
|
import type { NextFunction, Request, Response } from "express"
|
||||||
|
|
||||||
|
import { MedusaPricingContext, RequestQueryFields } from "@medusajs/types"
|
||||||
|
import * as core from "express-serve-static-core"
|
||||||
|
import { MedusaContainer } from "../container"
|
||||||
|
|
||||||
|
export interface FindConfig<Entity> {
|
||||||
|
select?: (keyof Entity)[]
|
||||||
|
skip?: number
|
||||||
|
take?: number
|
||||||
|
relations?: string[]
|
||||||
|
order?: { [K: string]: "ASC" | "DESC" }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List of all the supported HTTP methods
|
||||||
|
*/
|
||||||
|
export const HTTP_METHODS = [
|
||||||
|
"GET",
|
||||||
|
"POST",
|
||||||
|
"PUT",
|
||||||
|
"PATCH",
|
||||||
|
"DELETE",
|
||||||
|
"OPTIONS",
|
||||||
|
"HEAD",
|
||||||
|
] as const
|
||||||
|
|
||||||
|
export type RouteVerb = (typeof HTTP_METHODS)[number]
|
||||||
|
export type MiddlewareVerb = "USE" | "ALL" | RouteVerb
|
||||||
|
|
||||||
|
type SyncRouteHandler = (req: MedusaRequest, res: MedusaResponse) => void
|
||||||
|
|
||||||
|
export type AsyncRouteHandler = (
|
||||||
|
req: MedusaRequest,
|
||||||
|
res: MedusaResponse
|
||||||
|
) => Promise<void>
|
||||||
|
|
||||||
|
type RouteHandler = SyncRouteHandler | AsyncRouteHandler
|
||||||
|
|
||||||
|
export type RouteImplementation = {
|
||||||
|
method?: RouteVerb
|
||||||
|
handler: RouteHandler
|
||||||
|
}
|
||||||
|
|
||||||
|
export type RouteConfig = {
|
||||||
|
optedOutOfAuth?: boolean
|
||||||
|
routeType?: "admin" | "store" | "auth"
|
||||||
|
shouldAppendAdminCors?: boolean
|
||||||
|
shouldAppendStoreCors?: boolean
|
||||||
|
shouldAppendAuthCors?: boolean
|
||||||
|
routes?: RouteImplementation[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export type MiddlewareFunction =
|
||||||
|
| MedusaRequestHandler
|
||||||
|
| ((...args: any[]) => any)
|
||||||
|
|
||||||
|
export type MedusaErrorHandlerFunction = (
|
||||||
|
error: any,
|
||||||
|
req: MedusaRequest,
|
||||||
|
res: MedusaResponse,
|
||||||
|
next: MedusaNextFunction
|
||||||
|
) => Promise<void> | void
|
||||||
|
|
||||||
|
export type ParserConfigArgs = {
|
||||||
|
sizeLimit?: string | number | undefined
|
||||||
|
preserveRawBody?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ParserConfig = false | ParserConfigArgs
|
||||||
|
|
||||||
|
export type MiddlewareRoute = {
|
||||||
|
method?: MiddlewareVerb | MiddlewareVerb[]
|
||||||
|
matcher: string | RegExp
|
||||||
|
bodyParser?: ParserConfig
|
||||||
|
middlewares?: MiddlewareFunction[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export type MiddlewaresConfig = {
|
||||||
|
errorHandler?: false | MedusaErrorHandlerFunction
|
||||||
|
routes?: MiddlewareRoute[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export type RouteDescriptor = {
|
||||||
|
absolutePath: string
|
||||||
|
relativePath: string
|
||||||
|
route: string
|
||||||
|
priority: number
|
||||||
|
config?: RouteConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
export type GlobalMiddlewareDescriptor = {
|
||||||
|
config?: MiddlewaresConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface MedusaRequest<Body = unknown>
|
||||||
|
extends Request<core.ParamsDictionary, any, Body> {
|
||||||
|
validatedBody: Body
|
||||||
|
validatedQuery: RequestQueryFields & Record<string, unknown>
|
||||||
|
/**
|
||||||
|
* TODO: shouldn't this correspond to returnable fields instead of allowed fields? also it is used by the cleanResponseData util
|
||||||
|
*/
|
||||||
|
allowedProperties: string[]
|
||||||
|
/**
|
||||||
|
* An object containing the select, relation, skip, take and order to be used with medusa internal services
|
||||||
|
*/
|
||||||
|
listConfig: FindConfig<unknown>
|
||||||
|
/**
|
||||||
|
* An object containing the select, relation to be used with medusa internal services
|
||||||
|
*/
|
||||||
|
retrieveConfig: FindConfig<unknown>
|
||||||
|
/**
|
||||||
|
* An object containing fields and variables to be used with the remoteQuery
|
||||||
|
*/
|
||||||
|
remoteQueryConfig: {
|
||||||
|
fields: string[]
|
||||||
|
pagination: { order?: Record<string, string>; skip?: number; take?: number }
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* An object containing the fields that are filterable e.g `{ id: Any<String> }`
|
||||||
|
*/
|
||||||
|
filterableFields: Record<string, unknown>
|
||||||
|
includes?: Record<string, boolean>
|
||||||
|
/**
|
||||||
|
* An array of fields and relations that are allowed to be queried, this can be set by the
|
||||||
|
* consumer as part of a middleware and it will take precedence over the defaultAllowedFields
|
||||||
|
* @deprecated use `allowed` instead
|
||||||
|
*/
|
||||||
|
allowedFields?: string[]
|
||||||
|
/**
|
||||||
|
* An array of fields and relations that are allowed to be queried, this can be set by the
|
||||||
|
* consumer as part of a middleware and it will take precedence over the defaultAllowedFields set
|
||||||
|
* by the api
|
||||||
|
*/
|
||||||
|
allowed?: string[]
|
||||||
|
errors: string[]
|
||||||
|
scope: MedusaContainer
|
||||||
|
session?: any
|
||||||
|
rawBody?: any
|
||||||
|
requestId?: string
|
||||||
|
/**
|
||||||
|
* An object that carries the context that is used to calculate prices for variants
|
||||||
|
*/
|
||||||
|
pricingContext?: MedusaPricingContext
|
||||||
|
/**
|
||||||
|
* A generic context object that can be used across the request lifecycle
|
||||||
|
*/
|
||||||
|
context?: Record<string, any>
|
||||||
|
/**
|
||||||
|
* Custom validators for the request body and query params that will be
|
||||||
|
* merged with the original validator of the route.
|
||||||
|
*/
|
||||||
|
extendedValidators?: {
|
||||||
|
body?: ZodObject<any, any>
|
||||||
|
queryParams?: ZodObject<any, any>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AuthContext {
|
||||||
|
actor_id: string
|
||||||
|
actor_type: string
|
||||||
|
auth_identity_id: string
|
||||||
|
app_metadata: Record<string, unknown>
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AuthenticatedMedusaRequest<Body = never>
|
||||||
|
extends MedusaRequest<Body> {
|
||||||
|
auth_context: AuthContext
|
||||||
|
}
|
||||||
|
|
||||||
|
export type MedusaResponse<Body = unknown> = Response<Body>
|
||||||
|
|
||||||
|
export type MedusaNextFunction = NextFunction
|
||||||
|
|
||||||
|
export type MedusaRequestHandler<Body = unknown, Res = unknown> = (
|
||||||
|
req: MedusaRequest<Body>,
|
||||||
|
res: MedusaResponse<Res>,
|
||||||
|
next: MedusaNextFunction
|
||||||
|
) => Promise<void> | void
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
import {
|
||||||
|
MedusaNextFunction,
|
||||||
|
MedusaRequest,
|
||||||
|
MedusaRequestHandler,
|
||||||
|
MedusaResponse,
|
||||||
|
MiddlewaresConfig,
|
||||||
|
MiddlewareVerb,
|
||||||
|
ParserConfig,
|
||||||
|
} from "../types"
|
||||||
|
import { ZodObject } from "zod"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A helper function to configure the routes by defining custom middleware,
|
||||||
|
* bodyparser config and validators to be merged with the pre-existing
|
||||||
|
* route validators.
|
||||||
|
*/
|
||||||
|
export function defineMiddlewares<
|
||||||
|
Route extends {
|
||||||
|
method?: MiddlewareVerb | MiddlewareVerb[]
|
||||||
|
matcher: string | RegExp
|
||||||
|
bodyParser?: ParserConfig
|
||||||
|
extendedValidators?: {
|
||||||
|
body?: ZodObject<any, any>
|
||||||
|
queryParams?: ZodObject<any, any>
|
||||||
|
}
|
||||||
|
// eslint-disable-next-line space-before-function-paren
|
||||||
|
middlewares?: (<Req extends MedusaRequest>(
|
||||||
|
req: Req,
|
||||||
|
res: MedusaResponse,
|
||||||
|
next: MedusaNextFunction
|
||||||
|
) => any)[]
|
||||||
|
}
|
||||||
|
>(
|
||||||
|
config:
|
||||||
|
| Route[]
|
||||||
|
| { routes?: Route[]; errorHandler?: MiddlewaresConfig["errorHandler"] }
|
||||||
|
): MiddlewaresConfig {
|
||||||
|
const routes = Array.isArray(config) ? config : config.routes || []
|
||||||
|
const errorHandler = Array.isArray(config) ? undefined : config.errorHandler
|
||||||
|
|
||||||
|
return {
|
||||||
|
errorHandler,
|
||||||
|
routes: routes.map((route) => {
|
||||||
|
const { middlewares, extendedValidators, ...rest } = route
|
||||||
|
const customMiddleware: MedusaRequestHandler[] = []
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Define a custom validator when "extendedValidators.body" or
|
||||||
|
* "extendedValidators.queryParams" validation schema is
|
||||||
|
* provided.
|
||||||
|
*/
|
||||||
|
if (extendedValidators?.body || extendedValidators?.queryParams) {
|
||||||
|
customMiddleware.push((req, _, next) => {
|
||||||
|
req.extendedValidators = extendedValidators
|
||||||
|
next()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
...rest,
|
||||||
|
middlewares: customMiddleware.concat(middlewares || []),
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import * as QueryConfig from "./query-config"
|
import * as QueryConfig from "./query-config"
|
||||||
|
|
||||||
import { MiddlewareRoute } from "../../../loaders/helpers/routing/types"
|
import { MiddlewareRoute } from "@medusajs/framework"
|
||||||
import { validateAndTransformQuery } from "../../utils/validate-query"
|
import { validateAndTransformQuery } from "../../utils/validate-query"
|
||||||
import {
|
import {
|
||||||
AdminCreateApiKey,
|
AdminCreateApiKey,
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { MiddlewareRoute } from "../../../loaders/helpers/routing/types"
|
import { MiddlewareRoute } from "@medusajs/framework"
|
||||||
import { validateAndTransformBody } from "../../utils/validate-body"
|
import { validateAndTransformBody } from "../../utils/validate-body"
|
||||||
import { validateAndTransformQuery } from "../../utils/validate-query"
|
import { validateAndTransformQuery } from "../../utils/validate-query"
|
||||||
import { createLinkBody } from "../../utils/validators"
|
import { createLinkBody } from "../../utils/validators"
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { MiddlewareRoute } from "../../../loaders/helpers/routing/types"
|
import { MiddlewareRoute } from "@medusajs/framework"
|
||||||
import { validateAndTransformBody } from "../../utils/validate-body"
|
import { validateAndTransformBody } from "../../utils/validate-body"
|
||||||
import { validateAndTransformQuery } from "../../utils/validate-query"
|
import { validateAndTransformQuery } from "../../utils/validate-query"
|
||||||
import * as QueryConfig from "./query-config"
|
import * as QueryConfig from "./query-config"
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import * as QueryConfig from "./query-config"
|
import * as QueryConfig from "./query-config"
|
||||||
import { MiddlewareRoute } from "../../../loaders/helpers/routing/types"
|
import { MiddlewareRoute } from "@medusajs/framework"
|
||||||
import { validateAndTransformQuery } from "../../utils/validate-query"
|
import { validateAndTransformQuery } from "../../utils/validate-query"
|
||||||
import {
|
import {
|
||||||
AdminCreateCollection,
|
AdminCreateCollection,
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { MiddlewareRoute } from "../../../loaders/helpers/routing/types"
|
import { MiddlewareRoute } from "@medusajs/framework"
|
||||||
import { validateAndTransformQuery } from "../../utils/validate-query"
|
import { validateAndTransformQuery } from "../../utils/validate-query"
|
||||||
import * as QueryConfig from "./query-config"
|
import * as QueryConfig from "./query-config"
|
||||||
import { AdminGetCurrencyParams, AdminGetCurrenciesParams } from "./validators"
|
import { AdminGetCurrencyParams, AdminGetCurrenciesParams } from "./validators"
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { MiddlewareRoute } from "../../../loaders/helpers/routing/types"
|
import { MiddlewareRoute } from "@medusajs/framework"
|
||||||
import { validateAndTransformBody } from "../../utils/validate-body"
|
import { validateAndTransformBody } from "../../utils/validate-body"
|
||||||
import { validateAndTransformQuery } from "../../utils/validate-query"
|
import { validateAndTransformQuery } from "../../utils/validate-query"
|
||||||
import { createLinkBody } from "../../utils/validators"
|
import { createLinkBody } from "../../utils/validators"
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import {
|
|||||||
AdminUpdateCustomerAddress,
|
AdminUpdateCustomerAddress,
|
||||||
} from "./validators"
|
} from "./validators"
|
||||||
|
|
||||||
import { MiddlewareRoute } from "../../../loaders/helpers/routing/types"
|
import { MiddlewareRoute } from "@medusajs/framework"
|
||||||
import { validateAndTransformBody } from "../../utils/validate-body"
|
import { validateAndTransformBody } from "../../utils/validate-body"
|
||||||
import { validateAndTransformQuery } from "../../utils/validate-query"
|
import { validateAndTransformQuery } from "../../utils/validate-query"
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { MiddlewareRoute } from "../../../loaders/helpers/routing/types"
|
import { MiddlewareRoute } from "@medusajs/framework"
|
||||||
import { validateAndTransformBody } from "../../utils/validate-body"
|
import { validateAndTransformBody } from "../../utils/validate-body"
|
||||||
import { validateAndTransformQuery } from "../../utils/validate-query"
|
import { validateAndTransformQuery } from "../../utils/validate-query"
|
||||||
import * as QueryConfig from "./query-config"
|
import * as QueryConfig from "./query-config"
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { MiddlewareRoute } from "../../../types/middlewares"
|
import { MiddlewareRoute } from "@medusajs/framework"
|
||||||
import { maybeApplyLinkFilter } from "../../utils/maybe-apply-link-filter"
|
import { maybeApplyLinkFilter } from "../../utils/maybe-apply-link-filter"
|
||||||
import { validateAndTransformQuery } from "../../utils/validate-query"
|
import { validateAndTransformQuery } from "../../utils/validate-query"
|
||||||
import * as QueryConfig from "./query-config"
|
import * as QueryConfig from "./query-config"
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { MiddlewareRoute } from "../../../types/middlewares"
|
import { MiddlewareRoute } from "@medusajs/framework"
|
||||||
import { validateAndTransformBody } from "../../utils/validate-body"
|
import { validateAndTransformBody } from "../../utils/validate-body"
|
||||||
import { validateAndTransformQuery } from "../../utils/validate-query"
|
import { validateAndTransformQuery } from "../../utils/validate-query"
|
||||||
import * as QueryConfig from "./query-config"
|
import * as QueryConfig from "./query-config"
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { MiddlewareRoute } from "../../../types/middlewares"
|
import { MiddlewareRoute } from "@medusajs/framework"
|
||||||
import { validateAndTransformBody } from "../../utils/validate-body"
|
import { validateAndTransformBody } from "../../utils/validate-body"
|
||||||
import { validateAndTransformQuery } from "../../utils/validate-query"
|
import { validateAndTransformQuery } from "../../utils/validate-query"
|
||||||
import * as QueryConfig from "./query-config"
|
import * as QueryConfig from "./query-config"
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import * as QueryConfig from "./query-config"
|
import * as QueryConfig from "./query-config"
|
||||||
import { MiddlewareRoute } from "../../../types/middlewares"
|
import { MiddlewareRoute } from "@medusajs/framework"
|
||||||
import { validateAndTransformQuery } from "../../utils/validate-query"
|
import { validateAndTransformQuery } from "../../utils/validate-query"
|
||||||
import {
|
import {
|
||||||
AdminCreateInventoryItem,
|
AdminCreateInventoryItem,
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import {
|
|||||||
AdminInviteAccept,
|
AdminInviteAccept,
|
||||||
} from "./validators"
|
} from "./validators"
|
||||||
|
|
||||||
import { MiddlewareRoute } from "../../../types/middlewares"
|
import { MiddlewareRoute } from "@medusajs/framework"
|
||||||
import { authenticate } from "../../../utils/middlewares/authenticate-middleware"
|
import { authenticate } from "../../../utils/middlewares/authenticate-middleware"
|
||||||
import { validateAndTransformQuery } from "../../utils/validate-query"
|
import { validateAndTransformQuery } from "../../utils/validate-query"
|
||||||
import { validateAndTransformBody } from "../../utils/validate-body"
|
import { validateAndTransformBody } from "../../utils/validate-body"
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { MiddlewareRoute } from "../../../loaders/helpers/routing/types"
|
import { MiddlewareRoute } from "@medusajs/framework"
|
||||||
import { validateAndTransformQuery } from "../../utils/validate-query"
|
import { validateAndTransformQuery } from "../../utils/validate-query"
|
||||||
import * as QueryConfig from "./query-config"
|
import * as QueryConfig from "./query-config"
|
||||||
import {
|
import {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { MiddlewareRoute } from "../../../loaders/helpers/routing/types"
|
import { MiddlewareRoute } from "@medusajs/framework"
|
||||||
import { validateAndTransformBody } from "../../utils/validate-body"
|
import { validateAndTransformBody } from "../../utils/validate-body"
|
||||||
import { validateAndTransformQuery } from "../../utils/validate-query"
|
import { validateAndTransformQuery } from "../../utils/validate-query"
|
||||||
import * as QueryConfig from "./query-config"
|
import * as QueryConfig from "./query-config"
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { MiddlewareRoute } from "../../../types/middlewares"
|
import { MiddlewareRoute } from "@medusajs/framework"
|
||||||
import { unlessPath } from "../../utils/unless-path"
|
import { unlessPath } from "../../utils/unless-path"
|
||||||
import { validateAndTransformBody } from "../../utils/validate-body"
|
import { validateAndTransformBody } from "../../utils/validate-body"
|
||||||
import { validateAndTransformQuery } from "../../utils/validate-query"
|
import { validateAndTransformQuery } from "../../utils/validate-query"
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { MiddlewareRoute } from "../../../loaders/helpers/routing/types"
|
import { MiddlewareRoute } from "@medusajs/framework"
|
||||||
import { validateAndTransformBody } from "../../utils/validate-body"
|
import { validateAndTransformBody } from "../../utils/validate-body"
|
||||||
import { validateAndTransformQuery } from "../../utils/validate-query"
|
import { validateAndTransformQuery } from "../../utils/validate-query"
|
||||||
import { createBatchBody, createLinkBody } from "../../utils/validators"
|
import { createBatchBody, createLinkBody } from "../../utils/validators"
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { MiddlewareRoute } from "../../../loaders/helpers/routing/types"
|
import { MiddlewareRoute } from "@medusajs/framework"
|
||||||
import { validateAndTransformBody } from "../../utils/validate-body"
|
import { validateAndTransformBody } from "../../utils/validate-body"
|
||||||
import { validateAndTransformQuery } from "../../utils/validate-query"
|
import { validateAndTransformQuery } from "../../utils/validate-query"
|
||||||
import * as QueryConfig from "./query-config"
|
import * as QueryConfig from "./query-config"
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { MiddlewareRoute } from "../../../loaders/helpers/routing/types"
|
import { MiddlewareRoute } from "@medusajs/framework"
|
||||||
import { validateAndTransformBody } from "../../utils/validate-body"
|
import { validateAndTransformBody } from "../../utils/validate-body"
|
||||||
import { validateAndTransformQuery } from "../../utils/validate-query"
|
import { validateAndTransformQuery } from "../../utils/validate-query"
|
||||||
import { createLinkBody } from "../../utils/validators"
|
import { createLinkBody } from "../../utils/validators"
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import * as QueryConfig from "./query-config"
|
import * as QueryConfig from "./query-config"
|
||||||
import { MiddlewareRoute } from "../../../loaders/helpers/routing/types"
|
import { MiddlewareRoute } from "@medusajs/framework"
|
||||||
import { validateAndTransformQuery } from "../../utils/validate-query"
|
import { validateAndTransformQuery } from "../../utils/validate-query"
|
||||||
import {
|
import {
|
||||||
AdminCreateProductTag,
|
AdminCreateProductTag,
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import * as QueryConfig from "./query-config"
|
import * as QueryConfig from "./query-config"
|
||||||
import { MiddlewareRoute } from "../../../loaders/helpers/routing/types"
|
import { MiddlewareRoute } from "@medusajs/framework"
|
||||||
import { validateAndTransformQuery } from "../../utils/validate-query"
|
import { validateAndTransformQuery } from "../../utils/validate-query"
|
||||||
import {
|
import {
|
||||||
AdminCreateProductType,
|
AdminCreateProductType,
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { MiddlewareRoute } from "../../../loaders/helpers/routing/types"
|
import { MiddlewareRoute } from "@medusajs/framework"
|
||||||
import { maybeApplyLinkFilter } from "../../utils/maybe-apply-link-filter"
|
import { maybeApplyLinkFilter } from "../../utils/maybe-apply-link-filter"
|
||||||
import { unlessPath } from "../../utils/unless-path"
|
import { unlessPath } from "../../utils/unless-path"
|
||||||
import { validateAndTransformBody } from "../../utils/validate-body"
|
import { validateAndTransformBody } from "../../utils/validate-body"
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { MiddlewareRoute } from "../../../loaders/helpers/routing/types"
|
import { MiddlewareRoute } from "@medusajs/framework"
|
||||||
import { unlessPath } from "../../utils/unless-path"
|
import { unlessPath } from "../../utils/unless-path"
|
||||||
import { validateAndTransformBody } from "../../utils/validate-body"
|
import { validateAndTransformBody } from "../../utils/validate-body"
|
||||||
import { validateAndTransformQuery } from "../../utils/validate-query"
|
import { validateAndTransformQuery } from "../../utils/validate-query"
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import * as QueryConfig from "./query-config"
|
import * as QueryConfig from "./query-config"
|
||||||
import { MiddlewareRoute } from "../../../loaders/helpers/routing/types"
|
import { MiddlewareRoute } from "@medusajs/framework"
|
||||||
import { validateAndTransformQuery } from "../../utils/validate-query"
|
import { validateAndTransformQuery } from "../../utils/validate-query"
|
||||||
import {
|
import {
|
||||||
AdminCreateRegion,
|
AdminCreateRegion,
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import * as QueryConfig from "./query-config"
|
import * as QueryConfig from "./query-config"
|
||||||
|
|
||||||
import { MiddlewareRoute } from "../../../loaders/helpers/routing/types"
|
import { MiddlewareRoute } from "@medusajs/framework"
|
||||||
import { validateAndTransformQuery } from "../../utils/validate-query"
|
import { validateAndTransformQuery } from "../../utils/validate-query"
|
||||||
import {
|
import {
|
||||||
AdminCreateReservation,
|
AdminCreateReservation,
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { MiddlewareRoute } from "../../../loaders/helpers/routing/types"
|
import { MiddlewareRoute } from "@medusajs/framework"
|
||||||
import { validateAndTransformBody } from "../../utils/validate-body"
|
import { validateAndTransformBody } from "../../utils/validate-body"
|
||||||
import { validateAndTransformQuery } from "../../utils/validate-query"
|
import { validateAndTransformQuery } from "../../utils/validate-query"
|
||||||
import * as QueryConfig from "./query-config"
|
import * as QueryConfig from "./query-config"
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { MiddlewareRoute } from "../../../loaders/helpers/routing/types"
|
import { MiddlewareRoute } from "@medusajs/framework"
|
||||||
import { validateAndTransformBody } from "../../utils/validate-body"
|
import { validateAndTransformBody } from "../../utils/validate-body"
|
||||||
import { validateAndTransformQuery } from "../../utils/validate-query"
|
import { validateAndTransformQuery } from "../../utils/validate-query"
|
||||||
import * as QueryConfig from "./query-config"
|
import * as QueryConfig from "./query-config"
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { MiddlewareRoute } from "../../../loaders/helpers/routing/types"
|
import { MiddlewareRoute } from "@medusajs/framework"
|
||||||
import { maybeApplyLinkFilter } from "../../utils/maybe-apply-link-filter"
|
import { maybeApplyLinkFilter } from "../../utils/maybe-apply-link-filter"
|
||||||
import { validateAndTransformBody } from "../../utils/validate-body"
|
import { validateAndTransformBody } from "../../utils/validate-body"
|
||||||
import { validateAndTransformQuery } from "../../utils/validate-query"
|
import { validateAndTransformQuery } from "../../utils/validate-query"
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { MiddlewareRoute } from "../../../loaders/helpers/routing/types"
|
import { MiddlewareRoute } from "@medusajs/framework"
|
||||||
import {
|
import {
|
||||||
AdminCreateShippingOption,
|
AdminCreateShippingOption,
|
||||||
AdminCreateShippingOptionRule,
|
AdminCreateShippingOptionRule,
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { MiddlewareRoute } from "../../../loaders/helpers/routing/types"
|
import { MiddlewareRoute } from "@medusajs/framework"
|
||||||
import { validateAndTransformBody } from "../../utils/validate-body"
|
import { validateAndTransformBody } from "../../utils/validate-body"
|
||||||
import { validateAndTransformQuery } from "../../utils/validate-query"
|
import { validateAndTransformQuery } from "../../utils/validate-query"
|
||||||
import {
|
import {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { MiddlewareRoute } from "../../../types/middlewares"
|
import { MiddlewareRoute } from "@medusajs/framework"
|
||||||
import { maybeApplyLinkFilter } from "../../utils/maybe-apply-link-filter"
|
import { maybeApplyLinkFilter } from "../../utils/maybe-apply-link-filter"
|
||||||
import { validateAndTransformBody } from "../../utils/validate-body"
|
import { validateAndTransformBody } from "../../utils/validate-body"
|
||||||
import { validateAndTransformQuery } from "../../utils/validate-query"
|
import { validateAndTransformQuery } from "../../utils/validate-query"
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { MiddlewareRoute } from "../../../loaders/helpers/routing/types"
|
import { MiddlewareRoute } from "@medusajs/framework"
|
||||||
import { validateAndTransformBody } from "../../utils/validate-body"
|
import { validateAndTransformBody } from "../../utils/validate-body"
|
||||||
import { validateAndTransformQuery } from "../../utils/validate-query"
|
import { validateAndTransformQuery } from "../../utils/validate-query"
|
||||||
import * as QueryConfig from "./query-config"
|
import * as QueryConfig from "./query-config"
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import {
|
|||||||
AdminUpdateTaxRate,
|
AdminUpdateTaxRate,
|
||||||
} from "./validators"
|
} from "./validators"
|
||||||
|
|
||||||
import { MiddlewareRoute } from "../../../loaders/helpers/routing/types"
|
import { MiddlewareRoute } from "@medusajs/framework"
|
||||||
import { validateAndTransformBody } from "../../utils/validate-body"
|
import { validateAndTransformBody } from "../../utils/validate-body"
|
||||||
|
|
||||||
export const adminTaxRateRoutesMiddlewares: MiddlewareRoute[] = [
|
export const adminTaxRateRoutesMiddlewares: MiddlewareRoute[] = [
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import {
|
|||||||
AdminGetTaxRegionsParams,
|
AdminGetTaxRegionsParams,
|
||||||
} from "./validators"
|
} from "./validators"
|
||||||
|
|
||||||
import { MiddlewareRoute } from "../../../loaders/helpers/routing/types"
|
import { MiddlewareRoute } from "@medusajs/framework"
|
||||||
import { validateAndTransformBody } from "../../utils/validate-body"
|
import { validateAndTransformBody } from "../../utils/validate-body"
|
||||||
import { validateAndTransformQuery } from "../../utils/validate-query"
|
import { validateAndTransformQuery } from "../../utils/validate-query"
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import multer from "multer"
|
import multer from "multer"
|
||||||
import { MiddlewareRoute } from "../../../loaders/helpers/routing/types"
|
import { MiddlewareRoute } from "@medusajs/framework"
|
||||||
import { validateAndTransformQuery } from "../../utils/validate-query"
|
import { validateAndTransformQuery } from "../../utils/validate-query"
|
||||||
import { retrieveUploadConfig } from "./query-config"
|
import { retrieveUploadConfig } from "./query-config"
|
||||||
import { AdminGetUploadParams } from "./validators"
|
import { AdminGetUploadParams } from "./validators"
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import {
|
|||||||
AdminUpdateUser,
|
AdminUpdateUser,
|
||||||
} from "./validators"
|
} from "./validators"
|
||||||
|
|
||||||
import { MiddlewareRoute } from "../../../types/middlewares"
|
import { MiddlewareRoute } from "@medusajs/framework"
|
||||||
import { authenticate } from "../../../utils/middlewares/authenticate-middleware"
|
import { authenticate } from "../../../utils/middlewares/authenticate-middleware"
|
||||||
import { validateAndTransformQuery } from "../../utils/validate-query"
|
import { validateAndTransformQuery } from "../../utils/validate-query"
|
||||||
import { validateAndTransformBody } from "../../utils/validate-body"
|
import { validateAndTransformBody } from "../../utils/validate-body"
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import {
|
|||||||
AdminGetWorkflowExecutionsParams,
|
AdminGetWorkflowExecutionsParams,
|
||||||
} from "./validators"
|
} from "./validators"
|
||||||
|
|
||||||
import { MiddlewareRoute } from "../../../loaders/helpers/routing/types"
|
import { MiddlewareRoute } from "@medusajs/framework"
|
||||||
import { validateAndTransformQuery } from "../../utils/validate-query"
|
import { validateAndTransformQuery } from "../../utils/validate-query"
|
||||||
import { validateAndTransformBody } from "../../utils/validate-body"
|
import { validateAndTransformBody } from "../../utils/validate-body"
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { MiddlewareRoute } from "../../types/middlewares"
|
import { MiddlewareRoute } from "@medusajs/framework"
|
||||||
import { authenticate } from "../../utils/middlewares/authenticate-middleware"
|
import { authenticate } from "../../utils/middlewares/authenticate-middleware"
|
||||||
|
|
||||||
export const authRoutesMiddlewares: MiddlewareRoute[] = [
|
export const authRoutesMiddlewares: MiddlewareRoute[] = [
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { MiddlewareRoute } from "../../types/middlewares"
|
import { MiddlewareRoute } from "@medusajs/framework"
|
||||||
|
|
||||||
export const hooksRoutesMiddlewares: MiddlewareRoute[] = [
|
export const hooksRoutesMiddlewares: MiddlewareRoute[] = [
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { MiddlewareRoute } from "../../../loaders/helpers/routing/types"
|
import { MiddlewareRoute } from "@medusajs/framework"
|
||||||
import { ensurePublishableKeyAndSalesChannelMatch } from "../../utils/middlewares/common/ensure-pub-key-sales-channel-match"
|
import { ensurePublishableKeyAndSalesChannelMatch } from "../../utils/middlewares/common/ensure-pub-key-sales-channel-match"
|
||||||
import { maybeAttachPublishableKeyScopes } from "../../utils/middlewares/common/maybe-attach-pub-key-scopes"
|
import { maybeAttachPublishableKeyScopes } from "../../utils/middlewares/common/maybe-attach-pub-key-scopes"
|
||||||
import { validateAndTransformBody } from "../../utils/validate-body"
|
import { validateAndTransformBody } from "../../utils/validate-body"
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import * as QueryConfig from "./query-config"
|
import * as QueryConfig from "./query-config"
|
||||||
import { MiddlewareRoute } from "../../../loaders/helpers/routing/types"
|
import { MiddlewareRoute } from "@medusajs/framework"
|
||||||
import { validateAndTransformQuery } from "../../utils/validate-query"
|
import { validateAndTransformQuery } from "../../utils/validate-query"
|
||||||
import {
|
import {
|
||||||
StoreGetCollectionParams,
|
StoreGetCollectionParams,
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { MiddlewareRoute } from "../../../loaders/helpers/routing/types"
|
import { MiddlewareRoute } from "@medusajs/framework"
|
||||||
import { validateAndTransformQuery } from "../../utils/validate-query"
|
import { validateAndTransformQuery } from "../../utils/validate-query"
|
||||||
import * as QueryConfig from "./query-config"
|
import * as QueryConfig from "./query-config"
|
||||||
import { StoreGetCurrenciesParams, StoreGetCurrencyParams } from "./validators"
|
import { StoreGetCurrenciesParams, StoreGetCurrencyParams } from "./validators"
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import {
|
|||||||
StoreGetCustomerAddressParams,
|
StoreGetCustomerAddressParams,
|
||||||
} from "./validators"
|
} from "./validators"
|
||||||
|
|
||||||
import { MiddlewareRoute } from "../../../loaders/helpers/routing/types"
|
import { MiddlewareRoute } from "@medusajs/framework"
|
||||||
import { authenticate } from "../../../utils/middlewares/authenticate-middleware"
|
import { authenticate } from "../../../utils/middlewares/authenticate-middleware"
|
||||||
import { validateAndTransformBody } from "../../utils/validate-body"
|
import { validateAndTransformBody } from "../../utils/validate-body"
|
||||||
import { validateAndTransformQuery } from "../../utils/validate-query"
|
import { validateAndTransformQuery } from "../../utils/validate-query"
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { MiddlewareRoute } from "../../../loaders/helpers/routing/types"
|
import { MiddlewareRoute } from "@medusajs/framework"
|
||||||
import { authenticate } from "../../../utils/middlewares/authenticate-middleware"
|
import { authenticate } from "../../../utils/middlewares/authenticate-middleware"
|
||||||
import { validateAndTransformQuery } from "../../utils/validate-query"
|
import { validateAndTransformQuery } from "../../utils/validate-query"
|
||||||
import * as QueryConfig from "./query-config"
|
import * as QueryConfig from "./query-config"
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import { MiddlewareRoute } from "../../../types/middlewares"
|
import { MiddlewareRoute } from "@medusajs/framework"
|
||||||
import { validateAndTransformBody } from "../../utils/validate-body"
|
import { validateAndTransformBody } from "../../utils/validate-body"
|
||||||
import { validateAndTransformQuery } from "../../utils/validate-query"
|
import { validateAndTransformQuery } from "../../utils/validate-query"
|
||||||
import * as queryConfig from "./query-config"
|
import * as queryConfig from "./query-config"
|
||||||
import {
|
import {
|
||||||
StoreGetPaymentCollectionParams,
|
|
||||||
StoreCreatePaymentCollection,
|
StoreCreatePaymentCollection,
|
||||||
StoreCreatePaymentSession,
|
StoreCreatePaymentSession,
|
||||||
|
StoreGetPaymentCollectionParams,
|
||||||
} from "./validators"
|
} from "./validators"
|
||||||
|
|
||||||
export const storePaymentCollectionsMiddlewares: MiddlewareRoute[] = [
|
export const storePaymentCollectionsMiddlewares: MiddlewareRoute[] = [
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { MiddlewareRoute } from "../../../types/middlewares"
|
import { MiddlewareRoute } from "@medusajs/framework"
|
||||||
import { validateAndTransformQuery } from "../../utils/validate-query"
|
import { validateAndTransformQuery } from "../../utils/validate-query"
|
||||||
import * as queryConfig from "./query-config"
|
import * as queryConfig from "./query-config"
|
||||||
import { StoreGetPaymentProvidersParams } from "./validators"
|
import { StoreGetPaymentProvidersParams } from "./validators"
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { MiddlewareRoute } from "../../../loaders/helpers/routing/types"
|
import { MiddlewareRoute } from "@medusajs/framework"
|
||||||
import { validateAndTransformQuery } from "../../utils/validate-query"
|
import { validateAndTransformQuery } from "../../utils/validate-query"
|
||||||
import { applyCategoryFilters } from "./helpers"
|
import { applyCategoryFilters } from "./helpers"
|
||||||
import * as QueryConfig from "./query-config"
|
import * as QueryConfig from "./query-config"
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { isPresent, ProductStatus } from "@medusajs/utils"
|
import { isPresent, ProductStatus } from "@medusajs/utils"
|
||||||
import { MiddlewareRoute } from "../../../loaders/helpers/routing/types"
|
import { MiddlewareRoute } from "@medusajs/framework"
|
||||||
import { maybeApplyLinkFilter } from "../../utils/maybe-apply-link-filter"
|
import { maybeApplyLinkFilter } from "../../utils/maybe-apply-link-filter"
|
||||||
import {
|
import {
|
||||||
applyDefaultFilters,
|
applyDefaultFilters,
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { MiddlewareRoute } from "../../../loaders/helpers/routing/types"
|
import { MiddlewareRoute } from "@medusajs/framework"
|
||||||
import { validateAndTransformQuery } from "../../utils/validate-query"
|
import { validateAndTransformQuery } from "../../utils/validate-query"
|
||||||
import * as QueryConfig from "./query-config"
|
import * as QueryConfig from "./query-config"
|
||||||
import { StoreGetRegionParams, StoreGetRegionsParams } from "./validators"
|
import { StoreGetRegionParams, StoreGetRegionsParams } from "./validators"
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { MiddlewareRoute } from "../../../loaders/helpers/routing/types"
|
import { MiddlewareRoute } from "@medusajs/framework"
|
||||||
import { validateAndTransformQuery } from "../../utils/validate-query"
|
import { validateAndTransformQuery } from "../../utils/validate-query"
|
||||||
import * as QueryConfig from "./query-config"
|
import * as QueryConfig from "./query-config"
|
||||||
import { StoreReturnReasonParams } from "./validators"
|
import { StoreReturnReasonParams } from "./validators"
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { MiddlewareRoute } from "../../../loaders/helpers/routing/types"
|
import { MiddlewareRoute } from "@medusajs/framework"
|
||||||
import { validateAndTransformBody } from "../../utils/validate-body"
|
import { validateAndTransformBody } from "../../utils/validate-body"
|
||||||
import { validateAndTransformQuery } from "../../utils/validate-query"
|
import { validateAndTransformQuery } from "../../utils/validate-query"
|
||||||
import * as QueryConfig from "./query-config"
|
import * as QueryConfig from "./query-config"
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { MiddlewareRoute } from "../../../loaders/helpers/routing/types"
|
import { MiddlewareRoute } from "@medusajs/framework"
|
||||||
import { validateAndTransformQuery } from "../../utils/validate-query"
|
import { validateAndTransformQuery } from "../../utils/validate-query"
|
||||||
import { listTransformQueryConfig } from "./query-config"
|
import { listTransformQueryConfig } from "./query-config"
|
||||||
import { StoreGetShippingOptions } from "./validators"
|
import { StoreGetShippingOptions } from "./validators"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { NextFunction } from "express"
|
import { NextFunction } from "express"
|
||||||
import { MedusaRequest, MedusaResponse } from "../../types/routing"
|
import { MedusaRequest, MedusaResponse } from "../../types/routing"
|
||||||
import { MiddlewareFunction } from "../../types/middlewares"
|
import { MiddlewareFunction } from "@medusajs/framework"
|
||||||
|
|
||||||
// Due to how our route loader works, where we load all middlewares before routes, ambiguous routes end up having all middlewares on different routes executed before the route handler is.
|
// Due to how our route loader works, where we load all middlewares before routes, ambiguous routes end up having all middlewares on different routes executed before the route handler is.
|
||||||
// This function allows us to skip middlewares for particular routes, so we can temporarily solve this without completely breaking the route loader for everyone.
|
// This function allows us to skip middlewares for particular routes, so we can temporarily solve this without completely breaking the route loader for everyone.
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
import { ContainerRegistrationKeys } from "@medusajs/utils"
|
|
||||||
import { Express } from "express"
|
import { Express } from "express"
|
||||||
import path from "path"
|
import { join } from "path"
|
||||||
import qs from "qs"
|
import qs from "qs"
|
||||||
import { RoutesLoader } from "./helpers/routing"
|
import { RoutesLoader } from "@medusajs/framework"
|
||||||
import { MedusaContainer, PluginDetails } from "@medusajs/types"
|
import { MedusaContainer, PluginDetails } from "@medusajs/types"
|
||||||
|
|
||||||
type Options = {
|
type Options = {
|
||||||
@@ -24,9 +23,7 @@ export default async ({ app, container, plugins }: Options) => {
|
|||||||
next()
|
next()
|
||||||
})
|
})
|
||||||
|
|
||||||
const configModule = container.resolve(
|
const sourcePaths: string[] = []
|
||||||
ContainerRegistrationKeys.CONFIG_MODULE
|
|
||||||
)
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Always load plugin routes before the Medusa core routes, since it
|
* Always load plugin routes before the Medusa core routes, since it
|
||||||
@@ -40,31 +37,27 @@ export default async ({ app, container, plugins }: Options) => {
|
|||||||
* route will never be resolved, because it will be handled by the
|
* route will never be resolved, because it will be handled by the
|
||||||
* "/products/:id" route.
|
* "/products/:id" route.
|
||||||
*/
|
*/
|
||||||
await Promise.all(
|
sourcePaths.push(
|
||||||
plugins.map(async (pluginDetails) => {
|
...plugins.map((pluginDetails) => {
|
||||||
return new RoutesLoader({
|
return join(pluginDetails.resolve, "api")
|
||||||
app: app,
|
}),
|
||||||
configModule,
|
/**
|
||||||
rootDir: path.join(pluginDetails.resolve, "api"),
|
* Register the Medusa CORE API routes using the file based routing.
|
||||||
}).load()
|
*/
|
||||||
})
|
join(__dirname, "../api")
|
||||||
)
|
)
|
||||||
|
|
||||||
// TODO: Figure out why this is causing issues with test when placed inside ./api.ts
|
// TODO: Figure out why this is causing issues with test when placed inside ./api.ts
|
||||||
// Adding this here temporarily
|
// Adding this here temporarily
|
||||||
// Test: (packages/medusa/src/api/routes/admin/currencies/update-currency.ts)
|
// Test: (packages/medusa/src/api/routes/admin/currencies/update-currency.ts)
|
||||||
try {
|
try {
|
||||||
/**
|
|
||||||
* Register the Medusa CORE API routes using the file based routing.
|
|
||||||
*/
|
|
||||||
await new RoutesLoader({
|
await new RoutesLoader({
|
||||||
app: app,
|
app: app,
|
||||||
rootDir: path.join(__dirname, "../api"),
|
sourceDir: sourcePaths,
|
||||||
configModule,
|
|
||||||
}).load()
|
}).load()
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
throw Error(
|
throw Error(
|
||||||
"An error occurred while registering Medusa Core API Routes. See error in logs for more details.",
|
"An error occurred while registering API Routes. See error in logs for more details.",
|
||||||
{ cause: err }
|
{ cause: err }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { FlagSettings } from "../../types/feature-flags"
|
import { FlagSettings } from "@medusajs/framework"
|
||||||
|
|
||||||
const AnalyticsFeatureFlag: FlagSettings = {
|
const AnalyticsFeatureFlag: FlagSettings = {
|
||||||
key: "analytics",
|
key: "analytics",
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { FlagSettings } from "../../types/feature-flags"
|
import { FlagSettings } from "@medusajs/framework"
|
||||||
|
|
||||||
const OrderEditingFeatureFlag: FlagSettings = {
|
const OrderEditingFeatureFlag: FlagSettings = {
|
||||||
key: "order_editing",
|
key: "order_editing",
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { FlagSettings } from "../../types/feature-flags"
|
import { FlagSettings } from "@medusajs/framework"
|
||||||
|
|
||||||
const ProductCategoryFeatureFlag: FlagSettings = {
|
const ProductCategoryFeatureFlag: FlagSettings = {
|
||||||
key: "product_categories",
|
key: "product_categories",
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { FlagSettings } from "../../types/feature-flags"
|
import { FlagSettings } from "@medusajs/framework"
|
||||||
|
|
||||||
const PublishableAPIKeysFeatureFlag: FlagSettings = {
|
const PublishableAPIKeysFeatureFlag: FlagSettings = {
|
||||||
key: "publishable_api_keys",
|
key: "publishable_api_keys",
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { FlagSettings } from "../../types/feature-flags"
|
import { FlagSettings } from "@medusajs/framework"
|
||||||
|
|
||||||
const SalesChannelFeatureFlag: FlagSettings = {
|
const SalesChannelFeatureFlag: FlagSettings = {
|
||||||
key: "sales_channels",
|
key: "sales_channels",
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { FlagSettings } from "../../types/feature-flags"
|
import { FlagSettings } from "@medusajs/framework"
|
||||||
|
|
||||||
const TaxInclusivePricingFeatureFlag: FlagSettings = {
|
const TaxInclusivePricingFeatureFlag: FlagSettings = {
|
||||||
key: "tax_inclusive_pricing",
|
key: "tax_inclusive_pricing",
|
||||||
|
|||||||
@@ -1,87 +0,0 @@
|
|||||||
import {
|
|
||||||
MedusaNextFunction,
|
|
||||||
MedusaRequest,
|
|
||||||
MedusaRequestHandler,
|
|
||||||
MedusaResponse,
|
|
||||||
} from "../../../types/routing"
|
|
||||||
|
|
||||||
/**
|
|
||||||
* List of all the supported HTTP methods
|
|
||||||
*/
|
|
||||||
export const HTTP_METHODS = [
|
|
||||||
"GET",
|
|
||||||
"POST",
|
|
||||||
"PUT",
|
|
||||||
"PATCH",
|
|
||||||
"DELETE",
|
|
||||||
"OPTIONS",
|
|
||||||
"HEAD",
|
|
||||||
] as const
|
|
||||||
|
|
||||||
export type RouteVerb = (typeof HTTP_METHODS)[number]
|
|
||||||
export type MiddlewareVerb = "USE" | "ALL" | RouteVerb
|
|
||||||
|
|
||||||
type SyncRouteHandler = (req: MedusaRequest, res: MedusaResponse) => void
|
|
||||||
|
|
||||||
export type AsyncRouteHandler = (
|
|
||||||
req: MedusaRequest,
|
|
||||||
res: MedusaResponse
|
|
||||||
) => Promise<void>
|
|
||||||
|
|
||||||
type RouteHandler = SyncRouteHandler | AsyncRouteHandler
|
|
||||||
|
|
||||||
export type RouteImplementation = {
|
|
||||||
method?: RouteVerb
|
|
||||||
handler: RouteHandler
|
|
||||||
}
|
|
||||||
|
|
||||||
export type RouteConfig = {
|
|
||||||
optedOutOfAuth?: boolean
|
|
||||||
routeType?: "admin" | "store" | "auth"
|
|
||||||
shouldAppendAdminCors?: boolean
|
|
||||||
shouldAppendStoreCors?: boolean
|
|
||||||
shouldAppendAuthCors?: boolean
|
|
||||||
routes?: RouteImplementation[]
|
|
||||||
}
|
|
||||||
|
|
||||||
export type MiddlewareFunction =
|
|
||||||
| MedusaRequestHandler
|
|
||||||
| ((...args: any[]) => any)
|
|
||||||
|
|
||||||
export type MedusaErrorHandlerFunction = (
|
|
||||||
error: any,
|
|
||||||
req: MedusaRequest,
|
|
||||||
res: MedusaResponse,
|
|
||||||
next: MedusaNextFunction
|
|
||||||
) => Promise<void> | void
|
|
||||||
|
|
||||||
export type ParserConfigArgs = {
|
|
||||||
sizeLimit?: string | number | undefined
|
|
||||||
preserveRawBody?: boolean
|
|
||||||
}
|
|
||||||
|
|
||||||
export type ParserConfig = false | ParserConfigArgs
|
|
||||||
|
|
||||||
export type MiddlewareRoute = {
|
|
||||||
method?: MiddlewareVerb | MiddlewareVerb[]
|
|
||||||
matcher: string | RegExp
|
|
||||||
bodyParser?: ParserConfig
|
|
||||||
middlewares?: MiddlewareFunction[]
|
|
||||||
}
|
|
||||||
|
|
||||||
export type MiddlewaresConfig = {
|
|
||||||
errorHandler?: false | MedusaErrorHandlerFunction
|
|
||||||
routes?: MiddlewareRoute[]
|
|
||||||
}
|
|
||||||
|
|
||||||
export type RouteDescriptor = {
|
|
||||||
absolutePath: string
|
|
||||||
relativePath: string
|
|
||||||
route: string
|
|
||||||
priority: number
|
|
||||||
config?: RouteConfig
|
|
||||||
}
|
|
||||||
|
|
||||||
export type GlobalMiddlewareDescriptor = {
|
|
||||||
config?: MiddlewaresConfig
|
|
||||||
}
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
export interface IFlagRouter {
|
|
||||||
isFeatureEnabled: (key: string) => boolean
|
|
||||||
listFlags: () => FeatureFlagsResponse
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @schema FeatureFlagsResponse
|
|
||||||
* type: array
|
|
||||||
* items:
|
|
||||||
* type: object
|
|
||||||
* required:
|
|
||||||
* - key
|
|
||||||
* - value
|
|
||||||
* properties:
|
|
||||||
* key:
|
|
||||||
* description: The key of the feature flag.
|
|
||||||
* type: string
|
|
||||||
* value:
|
|
||||||
* description: The value of the feature flag.
|
|
||||||
* type: boolean
|
|
||||||
*/
|
|
||||||
export type FeatureFlagsResponse = {
|
|
||||||
key: string
|
|
||||||
value: boolean | Record<string, boolean>
|
|
||||||
}[]
|
|
||||||
|
|
||||||
export type FlagSettings = {
|
|
||||||
key: string
|
|
||||||
description: string
|
|
||||||
env_key: string
|
|
||||||
default_val: boolean
|
|
||||||
}
|
|
||||||
@@ -2,4 +2,4 @@ export type {
|
|||||||
MiddlewareFunction,
|
MiddlewareFunction,
|
||||||
MiddlewareRoute,
|
MiddlewareRoute,
|
||||||
MiddlewaresConfig,
|
MiddlewaresConfig,
|
||||||
} from "../loaders/helpers/routing/types"
|
} from "@medusajs/framework"
|
||||||
@@ -9,6 +9,7 @@ import {
|
|||||||
import * as core from "express-serve-static-core"
|
import * as core from "express-serve-static-core"
|
||||||
import { FindConfig } from "./common"
|
import { FindConfig } from "./common"
|
||||||
|
|
||||||
|
// TODO this will be reqorked and move to the framework at a later point unless decided otherwise
|
||||||
export interface MedusaRequest<Body = unknown>
|
export interface MedusaRequest<Body = unknown>
|
||||||
extends Request<core.ParamsDictionary, any, Body> {
|
extends Request<core.ParamsDictionary, any, Body> {
|
||||||
validatedBody: Body
|
validatedBody: Body
|
||||||
|
|||||||
@@ -1,67 +1,8 @@
|
|||||||
import { ZodObject } from "zod"
|
import { defineMiddlewares as originalDefineMiddlewares } from "@medusajs/framework"
|
||||||
import {
|
|
||||||
MedusaRequest,
|
|
||||||
MedusaResponse,
|
|
||||||
MedusaNextFunction,
|
|
||||||
MedusaRequestHandler,
|
|
||||||
} from "../types/routing"
|
|
||||||
import {
|
|
||||||
ParserConfig,
|
|
||||||
MiddlewareVerb,
|
|
||||||
MiddlewaresConfig,
|
|
||||||
} from "../loaders/helpers/routing/types"
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A helper function to configure the routes by defining custom middleware,
|
* A helper function to configure the routes by defining custom middleware,
|
||||||
* bodyparser config and validators to be merged with the pre-existing
|
* bodyparser config and validators to be merged with the pre-existing
|
||||||
* route validators.
|
* route validators.
|
||||||
*/
|
*/
|
||||||
export function defineMiddlewares<
|
export const defineMiddlewares = originalDefineMiddlewares
|
||||||
Route extends {
|
|
||||||
method?: MiddlewareVerb | MiddlewareVerb[]
|
|
||||||
matcher: string | RegExp
|
|
||||||
bodyParser?: ParserConfig
|
|
||||||
extendedValidators?: {
|
|
||||||
body?: ZodObject<any, any>
|
|
||||||
queryParams?: ZodObject<any, any>
|
|
||||||
}
|
|
||||||
// eslint-disable-next-line space-before-function-paren
|
|
||||||
middlewares?: (<Req extends MedusaRequest>(
|
|
||||||
req: Req,
|
|
||||||
res: MedusaResponse,
|
|
||||||
next: MedusaNextFunction
|
|
||||||
) => any)[]
|
|
||||||
}
|
|
||||||
>(
|
|
||||||
config:
|
|
||||||
| Route[]
|
|
||||||
| { routes?: Route[]; errorHandler?: MiddlewaresConfig["errorHandler"] }
|
|
||||||
): MiddlewaresConfig {
|
|
||||||
const routes = Array.isArray(config) ? config : config.routes || []
|
|
||||||
const errorHandler = Array.isArray(config) ? undefined : config.errorHandler
|
|
||||||
|
|
||||||
return {
|
|
||||||
errorHandler,
|
|
||||||
routes: routes.map((route) => {
|
|
||||||
const { middlewares, extendedValidators, ...rest } = route
|
|
||||||
const customMiddleware: MedusaRequestHandler[] = []
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Define a custom validator when "extendedValidators.body" or
|
|
||||||
* "extendedValidators.queryParams" validation schema is
|
|
||||||
* provided.
|
|
||||||
*/
|
|
||||||
if (extendedValidators?.body || extendedValidators?.queryParams) {
|
|
||||||
customMiddleware.push((req, _, next) => {
|
|
||||||
req.extendedValidators = extendedValidators
|
|
||||||
next()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
...rest,
|
|
||||||
middlewares: customMiddleware.concat(middlewares || []),
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,56 +1,3 @@
|
|||||||
import { MedusaError } from "@medusajs/utils"
|
import { formatException as originalFormatException } from "@medusajs/framework"
|
||||||
|
|
||||||
export enum PostgresError {
|
export const formatException = originalFormatException
|
||||||
DUPLICATE_ERROR = "23505",
|
|
||||||
FOREIGN_KEY_ERROR = "23503",
|
|
||||||
SERIALIZATION_FAILURE = "40001",
|
|
||||||
NULL_VIOLATION = "23502",
|
|
||||||
}
|
|
||||||
|
|
||||||
export const formatException = (err): MedusaError => {
|
|
||||||
switch (err.code) {
|
|
||||||
case PostgresError.DUPLICATE_ERROR:
|
|
||||||
return new MedusaError(
|
|
||||||
MedusaError.Types.DUPLICATE_ERROR,
|
|
||||||
`${err.table.charAt(0).toUpperCase()}${err.table.slice(
|
|
||||||
1
|
|
||||||
)} with ${err.detail.slice(4).replace(/[()=]/g, (s) => {
|
|
||||||
return s === "=" ? " " : ""
|
|
||||||
})}`
|
|
||||||
)
|
|
||||||
case PostgresError.FOREIGN_KEY_ERROR: {
|
|
||||||
const matches =
|
|
||||||
/Key \(([\w-\d]+)\)=\(([\w-\d]+)\) is not present in table "(\w+)"/g.exec(
|
|
||||||
err.detail
|
|
||||||
)
|
|
||||||
|
|
||||||
if (matches?.length !== 4) {
|
|
||||||
return new MedusaError(
|
|
||||||
MedusaError.Types.NOT_FOUND,
|
|
||||||
JSON.stringify(matches)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
return new MedusaError(
|
|
||||||
MedusaError.Types.NOT_FOUND,
|
|
||||||
`${matches[3]?.charAt(0).toUpperCase()}${matches[3]?.slice(1)} with ${
|
|
||||||
matches[1]
|
|
||||||
} ${matches[2]} does not exist.`
|
|
||||||
)
|
|
||||||
}
|
|
||||||
case PostgresError.SERIALIZATION_FAILURE: {
|
|
||||||
return new MedusaError(
|
|
||||||
MedusaError.Types.CONFLICT,
|
|
||||||
err?.detail ?? err?.message
|
|
||||||
)
|
|
||||||
}
|
|
||||||
case PostgresError.NULL_VIOLATION: {
|
|
||||||
return new MedusaError(
|
|
||||||
MedusaError.Types.INVALID_DATA,
|
|
||||||
`Can't insert null value in field ${err?.column} on insert in table ${err?.table}`
|
|
||||||
)
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,203 +1,3 @@
|
|||||||
import { ApiKeyDTO, ConfigModule, IApiKeyModuleService } from "@medusajs/types"
|
import { authenticate as originalAuthenticate } from "@medusajs/framework"
|
||||||
import {
|
|
||||||
ContainerRegistrationKeys,
|
|
||||||
ModuleRegistrationName,
|
|
||||||
} from "@medusajs/utils"
|
|
||||||
import { NextFunction, RequestHandler } from "express"
|
|
||||||
import jwt, { JwtPayload } from "jsonwebtoken"
|
|
||||||
import {
|
|
||||||
AuthContext,
|
|
||||||
AuthenticatedMedusaRequest,
|
|
||||||
MedusaRequest,
|
|
||||||
MedusaResponse,
|
|
||||||
} from "../../types/routing"
|
|
||||||
|
|
||||||
const SESSION_AUTH = "session"
|
export const authenticate = originalAuthenticate
|
||||||
const BEARER_AUTH = "bearer"
|
|
||||||
const API_KEY_AUTH = "api-key"
|
|
||||||
|
|
||||||
// This is the only hard-coded actor type, as API keys have special handling for now. We could also generalize API keys to carry the actor type with them.
|
|
||||||
const ADMIN_ACTOR_TYPE = "user"
|
|
||||||
|
|
||||||
type AuthType = typeof SESSION_AUTH | typeof BEARER_AUTH | typeof API_KEY_AUTH
|
|
||||||
|
|
||||||
type MedusaSession = {
|
|
||||||
auth_context: AuthContext
|
|
||||||
}
|
|
||||||
|
|
||||||
export const authenticate = (
|
|
||||||
actorType: string | string[],
|
|
||||||
authType: AuthType | AuthType[],
|
|
||||||
options: { allowUnauthenticated?: boolean; allowUnregistered?: boolean } = {}
|
|
||||||
): RequestHandler => {
|
|
||||||
return async (
|
|
||||||
req: MedusaRequest,
|
|
||||||
res: MedusaResponse,
|
|
||||||
next: NextFunction
|
|
||||||
): Promise<void> => {
|
|
||||||
const authTypes = Array.isArray(authType) ? authType : [authType]
|
|
||||||
const actorTypes = Array.isArray(actorType) ? actorType : [actorType]
|
|
||||||
const req_ = req as AuthenticatedMedusaRequest
|
|
||||||
|
|
||||||
// We only allow authenticating using a secret API key on the admin
|
|
||||||
const isExclusivelyUser =
|
|
||||||
actorTypes.length === 1 && actorTypes[0] === ADMIN_ACTOR_TYPE
|
|
||||||
|
|
||||||
if (authTypes.includes(API_KEY_AUTH) && isExclusivelyUser) {
|
|
||||||
const apiKey = await getApiKeyInfo(req)
|
|
||||||
if (apiKey) {
|
|
||||||
req_.auth_context = {
|
|
||||||
actor_id: apiKey.id,
|
|
||||||
actor_type: "api-key",
|
|
||||||
auth_identity_id: "",
|
|
||||||
app_metadata: {},
|
|
||||||
}
|
|
||||||
|
|
||||||
return next()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// We try to extract the auth context either from the session or from a JWT token
|
|
||||||
let authContext: AuthContext | null = getAuthContextFromSession(
|
|
||||||
req.session,
|
|
||||||
authTypes,
|
|
||||||
actorTypes
|
|
||||||
)
|
|
||||||
|
|
||||||
if (!authContext) {
|
|
||||||
const { http } = req.scope.resolve<ConfigModule>(
|
|
||||||
ContainerRegistrationKeys.CONFIG_MODULE
|
|
||||||
).projectConfig
|
|
||||||
|
|
||||||
authContext = getAuthContextFromJwtToken(
|
|
||||||
req.headers.authorization,
|
|
||||||
http.jwtSecret!,
|
|
||||||
authTypes,
|
|
||||||
actorTypes
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the entity is authenticated, and it is a registered actor we can continue
|
|
||||||
if (authContext?.actor_id) {
|
|
||||||
req_.auth_context = authContext
|
|
||||||
return next()
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the entity is authenticated, but there is no registered actor yet, we can continue (eg. in the case of a user invite) if allow unregistered is set
|
|
||||||
if (authContext?.auth_identity_id && options.allowUnregistered) {
|
|
||||||
req_.auth_context = authContext
|
|
||||||
return next()
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we allow unauthenticated requests (i.e public endpoints), just continue
|
|
||||||
if (options.allowUnauthenticated) {
|
|
||||||
return next()
|
|
||||||
}
|
|
||||||
|
|
||||||
res.status(401).json({ message: "Unauthorized" })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const getApiKeyInfo = async (req: MedusaRequest): Promise<ApiKeyDTO | null> => {
|
|
||||||
const authHeader = req.headers.authorization
|
|
||||||
if (!authHeader) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
const [tokenType, token] = authHeader.split(" ")
|
|
||||||
if (tokenType.toLowerCase() !== "basic" || !token) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
// The token could have been base64 encoded, we want to decode it first.
|
|
||||||
let normalizedToken = token
|
|
||||||
if (!token.startsWith("sk_")) {
|
|
||||||
normalizedToken = Buffer.from(token, "base64").toString("utf-8")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Basic auth is defined as a username:password set, and since the token is set to the username we need to trim the colon
|
|
||||||
if (normalizedToken.endsWith(":")) {
|
|
||||||
normalizedToken = normalizedToken.slice(0, -1)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Secret tokens start with 'sk_', and if it doesn't it could be a user JWT or a malformed token
|
|
||||||
if (!normalizedToken.startsWith("sk_")) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
const apiKeyModule = req.scope.resolve(
|
|
||||||
ModuleRegistrationName.API_KEY
|
|
||||||
) as IApiKeyModuleService
|
|
||||||
try {
|
|
||||||
const apiKey = await apiKeyModule.authenticate(normalizedToken)
|
|
||||||
if (!apiKey) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
return apiKey
|
|
||||||
} catch (error) {
|
|
||||||
console.error(error)
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const getAuthContextFromSession = (
|
|
||||||
session: Partial<MedusaSession> = {},
|
|
||||||
authTypes: AuthType[],
|
|
||||||
actorTypes: string[]
|
|
||||||
): AuthContext | null => {
|
|
||||||
if (!authTypes.includes(SESSION_AUTH)) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
session.auth_context &&
|
|
||||||
(actorTypes.includes("*") ||
|
|
||||||
actorTypes.includes(session.auth_context.actor_type))
|
|
||||||
) {
|
|
||||||
return session.auth_context
|
|
||||||
}
|
|
||||||
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
const getAuthContextFromJwtToken = (
|
|
||||||
authHeader: string | undefined,
|
|
||||||
jwtSecret: string,
|
|
||||||
authTypes: AuthType[],
|
|
||||||
actorTypes: string[]
|
|
||||||
): AuthContext | null => {
|
|
||||||
if (!authTypes.includes(BEARER_AUTH)) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!authHeader) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
const re = /(\S+)\s+(\S+)/
|
|
||||||
const matches = authHeader.match(re)
|
|
||||||
|
|
||||||
// TODO: figure out how to obtain token (and store correct data in token)
|
|
||||||
if (matches) {
|
|
||||||
const tokenType = matches[1]
|
|
||||||
const token = matches[2]
|
|
||||||
if (tokenType.toLowerCase() === BEARER_AUTH) {
|
|
||||||
// get config jwt secret
|
|
||||||
// verify token and set authUser
|
|
||||||
try {
|
|
||||||
const verified = jwt.verify(token, jwtSecret) as JwtPayload
|
|
||||||
if (
|
|
||||||
actorTypes.includes("*") ||
|
|
||||||
actorTypes.includes(verified.actor_type)
|
|
||||||
) {
|
|
||||||
return verified as AuthContext
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
@@ -1,99 +1,3 @@
|
|||||||
import { NextFunction, Request, Response } from "express"
|
import { errorHandler as originalErrorHandler } from "@medusajs/framework"
|
||||||
|
|
||||||
import { MedusaError } from "@medusajs/utils"
|
export const errorHandler = originalErrorHandler
|
||||||
import { Logger } from "../../types/global"
|
|
||||||
import { formatException } from "../../utils"
|
|
||||||
|
|
||||||
const QUERY_RUNNER_RELEASED = "QueryRunnerAlreadyReleasedError"
|
|
||||||
const TRANSACTION_STARTED = "TransactionAlreadyStartedError"
|
|
||||||
const TRANSACTION_NOT_STARTED = "TransactionNotStartedError"
|
|
||||||
|
|
||||||
const API_ERROR = "api_error"
|
|
||||||
const INVALID_REQUEST_ERROR = "invalid_request_error"
|
|
||||||
const INVALID_STATE_ERROR = "invalid_state_error"
|
|
||||||
|
|
||||||
export default () => {
|
|
||||||
return (
|
|
||||||
err: MedusaError,
|
|
||||||
req: Request,
|
|
||||||
res: Response,
|
|
||||||
next: NextFunction
|
|
||||||
) => {
|
|
||||||
const logger: Logger = req.scope.resolve("logger")
|
|
||||||
|
|
||||||
err = formatException(err)
|
|
||||||
|
|
||||||
logger.error(err)
|
|
||||||
|
|
||||||
const errorType = err.type || err.name
|
|
||||||
|
|
||||||
const errObj = {
|
|
||||||
code: err.code,
|
|
||||||
type: err.type,
|
|
||||||
message: err.message,
|
|
||||||
}
|
|
||||||
|
|
||||||
let statusCode = 500
|
|
||||||
switch (errorType) {
|
|
||||||
case QUERY_RUNNER_RELEASED:
|
|
||||||
case TRANSACTION_STARTED:
|
|
||||||
case TRANSACTION_NOT_STARTED:
|
|
||||||
case MedusaError.Types.CONFLICT:
|
|
||||||
statusCode = 409
|
|
||||||
errObj.code = INVALID_STATE_ERROR
|
|
||||||
errObj.message =
|
|
||||||
"The request conflicted with another request. You may retry the request with the provided Idempotency-Key."
|
|
||||||
break
|
|
||||||
case MedusaError.Types.UNAUTHORIZED:
|
|
||||||
statusCode = 401
|
|
||||||
break
|
|
||||||
case MedusaError.Types.PAYMENT_AUTHORIZATION_ERROR:
|
|
||||||
statusCode = 422
|
|
||||||
break
|
|
||||||
case MedusaError.Types.DUPLICATE_ERROR:
|
|
||||||
statusCode = 422
|
|
||||||
errObj.code = INVALID_REQUEST_ERROR
|
|
||||||
break
|
|
||||||
case MedusaError.Types.NOT_ALLOWED:
|
|
||||||
case MedusaError.Types.INVALID_DATA:
|
|
||||||
statusCode = 400
|
|
||||||
break
|
|
||||||
case MedusaError.Types.NOT_FOUND:
|
|
||||||
statusCode = 404
|
|
||||||
break
|
|
||||||
case MedusaError.Types.DB_ERROR:
|
|
||||||
statusCode = 500
|
|
||||||
errObj.code = API_ERROR
|
|
||||||
break
|
|
||||||
case MedusaError.Types.UNEXPECTED_STATE:
|
|
||||||
case MedusaError.Types.INVALID_ARGUMENT:
|
|
||||||
break
|
|
||||||
default:
|
|
||||||
errObj.code = "unknown_error"
|
|
||||||
errObj.message = "An unknown error occurred."
|
|
||||||
errObj.type = "unknown_error"
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
res.status(statusCode).json(errObj)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @schema Error
|
|
||||||
* title: "Response Error"
|
|
||||||
* type: object
|
|
||||||
* properties:
|
|
||||||
* code:
|
|
||||||
* type: string
|
|
||||||
* description: A slug code to indicate the type of the error.
|
|
||||||
* enum: [invalid_state_error, invalid_request_error, api_error, unknown_error]
|
|
||||||
* message:
|
|
||||||
* type: string
|
|
||||||
* description: Description of the error that occurred.
|
|
||||||
* example: "first_name must be a string"
|
|
||||||
* type:
|
|
||||||
* type: string
|
|
||||||
* description: A slug indicating the type of the error.
|
|
||||||
* enum: [QueryRunnerAlreadyReleasedError, TransactionAlreadyStartedError, TransactionNotStartedError, conflict, unauthorized, payment_authorization_error, duplicate_error, not_allowed, invalid_data, not_found, database_error, unexpected_state, invalid_argument, unknown_error]
|
|
||||||
*/
|
|
||||||
Loaded 100 of 102 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user