From 037a434d2d57d2ca710a4fb5957612f80d66e670 Mon Sep 17 00:00:00 2001 From: belvikuha Date: Tue, 11 Nov 2025 13:35:26 +0200 Subject: [PATCH] update admin/merchants route --- CHANGELOG.md | 7 +++ package-lock.json | 4 +- package.json | 2 +- src/api/admin/merchants/middlewares.ts | 15 +++++- src/api/admin/merchants/query-config.ts | 3 ++ src/api/admin/merchants/route.ts | 36 +++++++++----- .../add-userId-filter-for-admin.ts | 24 ++++++++++ src/api/middlewares/add-userId-filter.ts | 18 +++++++ .../middlewares/maybe-apply-store-filter.ts | 47 +++++++++++++++++++ 9 files changed, 139 insertions(+), 17 deletions(-) create mode 100644 src/api/middlewares/add-userId-filter-for-admin.ts create mode 100644 src/api/middlewares/add-userId-filter.ts create mode 100644 src/api/middlewares/maybe-apply-store-filter.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 2cd2fc8..ec8c1e1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## 0.47.0 + +### Features + +- Update admin/merchants route. Now it call entity: "store" instead of "user_store". + - processing of store search params moved to middleware + ## 0.46.0 ### Misc diff --git a/package-lock.json b/package-lock.json index ad45d21..bb81f9d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@techlabi/medusa-marketplace-plugin", - "version": "0.46.0", + "version": "0.47.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@techlabi/medusa-marketplace-plugin", - "version": "0.46.0", + "version": "0.47.0", "license": "Apache 2.0", "dependencies": { "@medusajs/icons": "^2.11.1", diff --git a/package.json b/package.json index 7425192..d73bf02 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@techlabi/medusa-marketplace-plugin", - "version": "0.46.0", + "version": "0.47.0", "description": "A multi-vendor Medusa plugin", "author": "TechLabi", "license": "Apache 2.0", diff --git a/src/api/admin/merchants/middlewares.ts b/src/api/admin/merchants/middlewares.ts index ed4c408..8347bcf 100644 --- a/src/api/admin/merchants/middlewares.ts +++ b/src/api/admin/merchants/middlewares.ts @@ -1,12 +1,23 @@ import { validateAndTransformQuery } from "@medusajs/framework"; -import { MiddlewareRoute } from "@medusajs/framework/http"; +import { maybeApplyLinkFilter, MiddlewareRoute } from "@medusajs/framework/http"; import * as QueryConfig from "./query-config"; import { AdminGetMerchantsParams } from "./validators"; +import { addUserIdToFilterableFieldsForAdmin } from "../../middlewares/add-userId-filter-for-admin"; +import { maybeApplyStoreSearchFilter } from "../../middlewares/maybe-apply-store-filter"; export const adminMerchantsRoutesMiddlewares: MiddlewareRoute[] = [ { method: ["GET"], matcher: "/admin/merchants", - middlewares: [validateAndTransformQuery(AdminGetMerchantsParams, QueryConfig.listTransformQueryConfig)], + middlewares: [ + validateAndTransformQuery(AdminGetMerchantsParams, QueryConfig.listTransformQueryConfig), + addUserIdToFilterableFieldsForAdmin, + maybeApplyStoreSearchFilter(), + maybeApplyLinkFilter({ + entryPoint: "user_store", + resourceId: "store_id", + filterableField: "user_id", + }), + ], }, ]; diff --git a/src/api/admin/merchants/query-config.ts b/src/api/admin/merchants/query-config.ts index a3bf654..0ba3f04 100644 --- a/src/api/admin/merchants/query-config.ts +++ b/src/api/admin/merchants/query-config.ts @@ -1,4 +1,7 @@ +export const defaultMerchantsFields = ["id", "name", "created_at", "users.*"]; + export const listTransformQueryConfig = { + defaults: defaultMerchantsFields, defaultLimit: 10, isList: true, }; diff --git a/src/api/admin/merchants/route.ts b/src/api/admin/merchants/route.ts index acf98f4..10e629e 100644 --- a/src/api/admin/merchants/route.ts +++ b/src/api/admin/merchants/route.ts @@ -1,23 +1,35 @@ -import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"; +import { MedusaRequest, MedusaResponse, refetchEntities } from "@medusajs/framework/http"; import { UserDTO } from "@medusajs/framework/types"; -import { getMerchantsListWorkflow } from "../../../workflows/get-merchants-list"; import { AdminGetMerchantsParamsType } from "./validators"; export const GET = async (req: MedusaRequest, res: MedusaResponse) => { const loggedInUser = req.scope.resolve("loggedInUser") as UserDTO; - const { result } = await getMerchantsListWorkflow(req.scope).run({ - input: { - userId: loggedInUser.id, - isSuperAdmin: !!loggedInUser.metadata?.is_super_admin, - ...req.queryConfig.pagination, - ...req.filterableFields, + + const response = await refetchEntities({ + entity: "store", + idOrFilter: req?.filterableFields, + scope: req.scope, + fields: req.queryConfig?.fields, + pagination: { + ...req.queryConfig?.pagination, + order: { created_at: "DESC" }, }, }); - const { merchants, metadata } = result; + + const merchants = response.data.map((merchant) => ({ + ...merchant, + store_id: merchant.id, + user_id: merchant?.users[0]?.id, + store_name: merchant.name, + user_email: merchant?.users[0]?.email, + status: "active", + can_impersonate: !!loggedInUser.metadata?.is_super_admin, + })); + res.json({ merchants, - count: metadata.count, - offset: metadata.skip, - limit: metadata.take, + count: response.metadata.count, + offset: response.metadata.skip, + limit: response.metadata.take, }); }; diff --git a/src/api/middlewares/add-userId-filter-for-admin.ts b/src/api/middlewares/add-userId-filter-for-admin.ts new file mode 100644 index 0000000..8eea394 --- /dev/null +++ b/src/api/middlewares/add-userId-filter-for-admin.ts @@ -0,0 +1,24 @@ +import { type MedusaNextFunction, type MedusaRequest, type MedusaResponse } from "@medusajs/framework/http"; +import { UserDTO } from "@medusajs/framework/types"; +import { addUserIdToFilterableFields } from "./add-userId-filter"; + +export async function addUserIdToFilterableFieldsForAdmin( + req: MedusaRequest, + res: MedusaResponse, + next: MedusaNextFunction +) { + const loggedInUser = req.scope.resolve("loggedInUser", { + allowUnregistered: true, + }) as UserDTO; + if (!loggedInUser) { + return next(); + } + + const isSuperAdmin = loggedInUser.metadata?.is_super_admin && !req.session.impersonate_user_id; + + if (isSuperAdmin) { + return next(); + } + + return addUserIdToFilterableFields(req, res, next); +} diff --git a/src/api/middlewares/add-userId-filter.ts b/src/api/middlewares/add-userId-filter.ts new file mode 100644 index 0000000..a86ee56 --- /dev/null +++ b/src/api/middlewares/add-userId-filter.ts @@ -0,0 +1,18 @@ +import { type MedusaNextFunction, type MedusaRequest, type MedusaResponse } from "@medusajs/framework/http"; +import { UserDTO } from "@medusajs/framework/types"; + +export async function addUserIdToFilterableFields(req: MedusaRequest, res: MedusaResponse, next: MedusaNextFunction) { + const loggedInUser = req.scope.resolve("loggedInUser", { + allowUnregistered: true, + }) as UserDTO; + if (!loggedInUser) { + return next(); + } + + if (!req.filterableFields) { + req.filterableFields = {}; + } + + req.filterableFields["user_id"] = loggedInUser.id; + return next(); +} diff --git a/src/api/middlewares/maybe-apply-store-filter.ts b/src/api/middlewares/maybe-apply-store-filter.ts new file mode 100644 index 0000000..cd73a9f --- /dev/null +++ b/src/api/middlewares/maybe-apply-store-filter.ts @@ -0,0 +1,47 @@ +import { MedusaRequest } from "@medusajs/framework/http"; +import { OperatorMap } from "@medusajs/framework/types"; +import { ContainerRegistrationKeys } from "@medusajs/framework/utils"; +import { NextFunction } from "express"; + +type MerchantStoresFilter = { + q?: string; + store_id?: OperatorMap; + user_id?: string; + id?: string[] | string; +}; + +export function maybeApplyStoreSearchFilter() { + return async function applyDateRangeFilter(req: MedusaRequest, _, next: NextFunction) { + const filterableFields: MerchantStoresFilter = req.filterableFields; + + const query = req.scope.resolve(ContainerRegistrationKeys.QUERY); + const q = filterableFields.q; + delete filterableFields.q; + + if (!q) { + return next(); + } else { + const { data: users } = await query.graph({ + entity: "user", + fields: ["id", "email"], + filters: { + email: { $ilike: `%${q}%` }, + }, + }); + + const { data: usersStore } = await query.graph({ + entity: "user_store", + fields: ["id", "user_id", "store_id"], + filters: { + user_id: [...users.map(user => user.id)], + }, + }); + + Object.assign(filterableFields, { + $or: [{ id: [...usersStore.map(u => u.store_id)] }, { name: { $ilike: `%${q}%` } }], + }); + } + + return next(); + }; +}