@lodashventure/medusa-brand 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/README.md +95 -0
  2. package/dist/admin/components/brand-form.d.ts +19 -0
  3. package/dist/admin/components/brand-form.js +182 -0
  4. package/dist/admin/components/brand-image-uploader.d.ts +14 -0
  5. package/dist/admin/components/brand-image-uploader.js +217 -0
  6. package/dist/admin/lib/sdk.d.ts +1 -0
  7. package/dist/admin/lib/sdk.js +14 -0
  8. package/dist/admin/routes/brands/page.d.ts +4 -0
  9. package/dist/admin/routes/brands/page.js +253 -0
  10. package/dist/admin/widgets/product-brand-widget.d.ts +8 -0
  11. package/dist/admin/widgets/product-brand-widget.js +207 -0
  12. package/dist/api/admin/brands/[id]/image/route.d.ts +5 -0
  13. package/dist/api/admin/brands/[id]/image/route.js +118 -0
  14. package/dist/api/admin/brands/[id]/logo/route.d.ts +5 -0
  15. package/dist/api/admin/brands/[id]/logo/route.js +118 -0
  16. package/dist/api/admin/brands/[id]/products/route.d.ts +2 -0
  17. package/dist/api/admin/brands/[id]/products/route.js +51 -0
  18. package/dist/api/admin/brands/[id]/route.d.ts +5 -0
  19. package/dist/api/admin/brands/[id]/route.js +111 -0
  20. package/dist/api/admin/brands/route.d.ts +4 -0
  21. package/dist/api/admin/brands/route.js +75 -0
  22. package/dist/api/admin/products/[id]/brand/route.d.ts +5 -0
  23. package/dist/api/admin/products/[id]/brand/route.js +116 -0
  24. package/dist/api/middlewares/attach-brand-to-products.d.ts +2 -0
  25. package/dist/api/middlewares/attach-brand-to-products.js +104 -0
  26. package/dist/api/middlewares.d.ts +6 -0
  27. package/dist/api/middlewares.js +26 -0
  28. package/dist/api/store/brands/route.d.ts +2 -0
  29. package/dist/api/store/brands/route.js +50 -0
  30. package/dist/index.d.ts +1 -0
  31. package/dist/index.js +6 -0
  32. package/dist/modules/brand/index.d.ts +35 -0
  33. package/dist/modules/brand/index.js +12 -0
  34. package/dist/modules/brand/migrations/Migration20251021070648.d.ts +5 -0
  35. package/dist/modules/brand/migrations/Migration20251021070648.js +27 -0
  36. package/dist/modules/brand/models/brand.d.ts +16 -0
  37. package/dist/modules/brand/models/brand.js +42 -0
  38. package/dist/modules/brand/service.d.ts +21 -0
  39. package/dist/modules/brand/service.js +10 -0
  40. package/dist/services/gcs-direct-upload.d.ts +8 -0
  41. package/dist/services/gcs-direct-upload.js +54 -0
  42. package/dist/workflows/upload-brand-image.d.ts +15 -0
  43. package/dist/workflows/upload-brand-image.js +56 -0
  44. package/package.json +58 -0
@@ -0,0 +1,118 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DELETE = exports.POST = exports.GET = void 0;
4
+ const utils_1 = require("@medusajs/framework/utils");
5
+ const upload_brand_image_1 = require("../../../../../workflows/upload-brand-image");
6
+ const gcs_direct_upload_1 = require("../../../../../services/gcs-direct-upload");
7
+ // GET - Get brand logo
8
+ const GET = async (req, res) => {
9
+ const { id } = req.params;
10
+ const brandService = req.scope.resolve("brandCustom");
11
+ try {
12
+ const brand = await brandService.retrieveBrand(id);
13
+ if (!brand) {
14
+ return res.status(404).json({ error: "Brand not found" });
15
+ }
16
+ return res.json({
17
+ logo: brand.logo || null,
18
+ });
19
+ }
20
+ catch (error) {
21
+ console.error("Error fetching brand logo:", error);
22
+ return res.status(500).json({ error: "Failed to fetch brand logo" });
23
+ }
24
+ };
25
+ exports.GET = GET;
26
+ // POST - Upload brand logo
27
+ const POST = async (req, res) => {
28
+ const { id } = req.params;
29
+ const brandService = req.scope.resolve("brandCustom");
30
+ const logger = req.scope.resolve(utils_1.ContainerRegistrationKeys.LOGGER);
31
+ try {
32
+ const brand = await brandService.retrieveBrand(id);
33
+ if (!brand) {
34
+ return res.status(404).json({ error: "Brand not found" });
35
+ }
36
+ const file = req.file;
37
+ if (!file) {
38
+ return res.status(400).json({ error: "No file uploaded" });
39
+ }
40
+ // Validate file type
41
+ const allowedMimeTypes = ['image/jpeg', 'image/jpg', 'image/png', 'image/gif', 'image/webp', 'image/svg+xml'];
42
+ if (!allowedMimeTypes.includes(file.mimetype)) {
43
+ return res.status(400).json({
44
+ error: "Invalid file type. Only JPEG, PNG, GIF, WebP, and SVG images are allowed."
45
+ });
46
+ }
47
+ // Validate file size (max 2MB for logos)
48
+ if (file.size > 2 * 1024 * 1024) {
49
+ return res.status(400).json({
50
+ error: "File size too large. Maximum allowed size is 2MB."
51
+ });
52
+ }
53
+ const result = await (0, upload_brand_image_1.uploadBrandImageWorkflow)(req.scope).run({
54
+ input: {
55
+ brandId: id,
56
+ imageType: "logo",
57
+ fileData: {
58
+ filename: file.originalname,
59
+ mimeType: file.mimetype,
60
+ content: file.buffer.toString("base64"),
61
+ },
62
+ },
63
+ });
64
+ return res.json({
65
+ brand: {
66
+ id,
67
+ logo: result.result.imageUrl,
68
+ },
69
+ });
70
+ }
71
+ catch (error) {
72
+ logger.error("Error uploading brand logo:", error);
73
+ return res.status(500).json({ error: "Failed to upload brand logo" });
74
+ }
75
+ };
76
+ exports.POST = POST;
77
+ // DELETE - Delete brand logo
78
+ const DELETE = async (req, res) => {
79
+ const { id } = req.params;
80
+ const brandService = req.scope.resolve("brandCustom");
81
+ const logger = req.scope.resolve(utils_1.ContainerRegistrationKeys.LOGGER);
82
+ const gcsUploadService = new gcs_direct_upload_1.GcsDirectUploadService();
83
+ try {
84
+ const brand = await brandService.retrieveBrand(id);
85
+ if (!brand) {
86
+ return res.status(404).json({ error: "Brand not found" });
87
+ }
88
+ if (!brand.logo) {
89
+ return res.status(404).json({ error: "No logo found for this brand" });
90
+ }
91
+ // Delete the file from GCS
92
+ if (brand.logo.includes('storage.googleapis.com')) {
93
+ try {
94
+ const parts = brand.logo.split('/');
95
+ const bucketIndex = parts.indexOf('sangaroon');
96
+ if (bucketIndex !== -1 && bucketIndex < parts.length - 1) {
97
+ const filename = parts.slice(bucketIndex + 1).join('/');
98
+ await gcsUploadService.deleteFile(filename);
99
+ }
100
+ }
101
+ catch (deleteError) {
102
+ logger.error("Error deleting file from GCS:", deleteError);
103
+ // Continue even if file deletion fails
104
+ }
105
+ }
106
+ await brandService.updateBrands([
107
+ { id: brand.id, logo: null }
108
+ ]);
109
+ return res.json({
110
+ message: "Brand logo deleted successfully",
111
+ });
112
+ }
113
+ catch (error) {
114
+ logger.error("Error deleting brand logo:", error);
115
+ return res.status(500).json({ error: "Failed to delete brand logo" });
116
+ }
117
+ };
118
+ exports.DELETE = DELETE;
@@ -0,0 +1,2 @@
1
+ import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http";
2
+ export declare const GET: (req: MedusaRequest, res: MedusaResponse) => Promise<MedusaResponse>;
@@ -0,0 +1,51 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.GET = void 0;
4
+ const utils_1 = require("@medusajs/framework/utils");
5
+ // GET - Get all products for a brand
6
+ const GET = async (req, res) => {
7
+ const { id } = req.params;
8
+ const brandService = req.scope.resolve("brandCustom");
9
+ const productModuleService = req.scope.resolve(utils_1.Modules.PRODUCT);
10
+ const logger = req.scope.resolve(utils_1.ContainerRegistrationKeys.LOGGER);
11
+ try {
12
+ const { limit = 20, offset = 0, } = req.query;
13
+ // Verify brand exists
14
+ const brand = await brandService.retrieveBrand(id);
15
+ if (!brand) {
16
+ return res.status(404).json({ error: "Brand not found" });
17
+ }
18
+ // Get all product-brand associations for this brand
19
+ const productBrands = await brandService.listProductBrands({
20
+ brand_id: id,
21
+ });
22
+ if (productBrands.length === 0) {
23
+ return res.json({
24
+ products: [],
25
+ count: 0,
26
+ offset: Number(offset),
27
+ limit: Number(limit),
28
+ });
29
+ }
30
+ // Get product IDs
31
+ const productIds = productBrands.map((pb) => pb.product_id);
32
+ // Fetch products from product module
33
+ const products = await productModuleService.listProducts({
34
+ id: productIds,
35
+ });
36
+ const count = await productModuleService.listProducts({
37
+ id: productIds,
38
+ }).then((result) => result.length);
39
+ return res.json({
40
+ products,
41
+ count,
42
+ offset: Number(offset),
43
+ limit: Number(limit),
44
+ });
45
+ }
46
+ catch (error) {
47
+ logger.error(`Error getting products for brand ${id}:`, error);
48
+ return res.status(500).json({ error: "Failed to get brand products" });
49
+ }
50
+ };
51
+ exports.GET = GET;
@@ -0,0 +1,5 @@
1
+ import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http";
2
+ import { UpdateBrandRequest } from "../../../../types";
3
+ export declare const GET: (req: MedusaRequest, res: MedusaResponse) => Promise<MedusaResponse>;
4
+ export declare const PUT: (req: MedusaRequest<UpdateBrandRequest>, res: MedusaResponse) => Promise<MedusaResponse>;
5
+ export declare const DELETE: (req: MedusaRequest, res: MedusaResponse) => Promise<MedusaResponse>;
@@ -0,0 +1,111 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DELETE = exports.PUT = exports.GET = void 0;
4
+ const utils_1 = require("@medusajs/framework/utils");
5
+ const gcs_direct_upload_1 = require("../../../../services/gcs-direct-upload");
6
+ // GET - Get a specific brand by ID
7
+ const GET = async (req, res) => {
8
+ const { id } = req.params;
9
+ const brandService = req.scope.resolve("brandCustom");
10
+ const logger = req.scope.resolve(utils_1.ContainerRegistrationKeys.LOGGER);
11
+ try {
12
+ const brand = await brandService.retrieveBrand(id);
13
+ if (!brand) {
14
+ return res.status(404).json({ error: "Brand not found" });
15
+ }
16
+ return res.json({ brand });
17
+ }
18
+ catch (error) {
19
+ logger.error(`Error retrieving brand ${id}:`, error);
20
+ return res.status(500).json({ error: "Failed to retrieve brand" });
21
+ }
22
+ };
23
+ exports.GET = GET;
24
+ // PUT - Update a brand
25
+ const PUT = async (req, res) => {
26
+ const { id } = req.params;
27
+ const brandService = req.scope.resolve("brandCustom");
28
+ const logger = req.scope.resolve(utils_1.ContainerRegistrationKeys.LOGGER);
29
+ try {
30
+ const brand = await brandService.retrieveBrand(id);
31
+ if (!brand) {
32
+ return res.status(404).json({ error: "Brand not found" });
33
+ }
34
+ const { name, slug, description, website, is_active, metadata, } = req.body;
35
+ // Check if new slug conflicts with another brand
36
+ if (slug && slug !== brand.slug) {
37
+ const existingBrand = await brandService.listBrands({ slug }).then((result) => result[0]);
38
+ if (existingBrand && existingBrand.id !== id) {
39
+ return res.status(400).json({
40
+ error: "Brand with this slug already exists"
41
+ });
42
+ }
43
+ }
44
+ const updateData = {
45
+ id,
46
+ };
47
+ if (name !== undefined)
48
+ updateData.name = name;
49
+ if (slug !== undefined)
50
+ updateData.slug = slug;
51
+ if (description !== undefined)
52
+ updateData.description = description;
53
+ if (website !== undefined)
54
+ updateData.website = website;
55
+ if (is_active !== undefined)
56
+ updateData.is_active = is_active;
57
+ if (metadata !== undefined)
58
+ updateData.metadata = metadata;
59
+ const updatedBrand = await brandService.updateBrands([updateData]).then((result) => result[0]);
60
+ return res.json({ brand: updatedBrand });
61
+ }
62
+ catch (error) {
63
+ logger.error(`Error updating brand ${id}:`, error);
64
+ return res.status(500).json({ error: "Failed to update brand" });
65
+ }
66
+ };
67
+ exports.PUT = PUT;
68
+ // DELETE - Delete a brand
69
+ const DELETE = async (req, res) => {
70
+ const { id } = req.params;
71
+ const brandService = req.scope.resolve("brandCustom");
72
+ const logger = req.scope.resolve(utils_1.ContainerRegistrationKeys.LOGGER);
73
+ const gcsUploadService = new gcs_direct_upload_1.GcsDirectUploadService();
74
+ try {
75
+ const brand = await brandService.retrieveBrand(id);
76
+ if (!brand) {
77
+ return res.status(404).json({ error: "Brand not found" });
78
+ }
79
+ // Delete images from GCS if they exist
80
+ const imagesToDelete = [brand.image, brand.logo].filter(Boolean);
81
+ for (const imageUrl of imagesToDelete) {
82
+ if (imageUrl.includes('storage.googleapis.com')) {
83
+ try {
84
+ const parts = imageUrl.split('/');
85
+ const bucketIndex = parts.indexOf('sangaroon');
86
+ if (bucketIndex !== -1 && bucketIndex < parts.length - 1) {
87
+ const filename = parts.slice(bucketIndex + 1).join('/');
88
+ await gcsUploadService.deleteFile(filename);
89
+ }
90
+ }
91
+ catch (error) {
92
+ logger.error(`Failed to delete image ${imageUrl}:`, error);
93
+ // Continue even if image deletion fails
94
+ }
95
+ }
96
+ }
97
+ // Delete product-brand associations
98
+ const productBrands = await brandService.listProductBrands({ brand_id: id });
99
+ if (productBrands.length > 0) {
100
+ await brandService.deleteProductBrands(productBrands.map((pb) => pb.id));
101
+ }
102
+ // Delete the brand
103
+ await brandService.deleteBrands([id]);
104
+ return res.json({ message: "Brand deleted successfully" });
105
+ }
106
+ catch (error) {
107
+ logger.error(`Error deleting brand ${id}:`, error);
108
+ return res.status(500).json({ error: "Failed to delete brand" });
109
+ }
110
+ };
111
+ exports.DELETE = DELETE;
@@ -0,0 +1,4 @@
1
+ import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http";
2
+ import { CreateBrandRequest } from "../../../types";
3
+ export declare const GET: (req: MedusaRequest, res: MedusaResponse) => Promise<MedusaResponse>;
4
+ export declare const POST: (req: MedusaRequest<CreateBrandRequest>, res: MedusaResponse) => Promise<MedusaResponse>;
@@ -0,0 +1,75 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.POST = exports.GET = void 0;
4
+ const utils_1 = require("@medusajs/framework/utils");
5
+ // GET - List all brands with filters
6
+ const GET = async (req, res) => {
7
+ const brandService = req.scope.resolve("brandCustom");
8
+ const logger = req.scope.resolve(utils_1.ContainerRegistrationKeys.LOGGER);
9
+ try {
10
+ const { is_active, q, limit = 20, offset = 0, } = req.query;
11
+ const filters = {};
12
+ if (is_active !== undefined) {
13
+ filters.is_active = is_active === 'true';
14
+ }
15
+ if (q) {
16
+ filters.$or = [
17
+ { name: { $ilike: `%${q}%` } },
18
+ { description: { $ilike: `%${q}%` } },
19
+ ];
20
+ }
21
+ const [brands, count] = await Promise.all([
22
+ brandService.listBrands(filters, {
23
+ limit: Number(limit),
24
+ offset: Number(offset),
25
+ order: { created_at: "DESC" },
26
+ }),
27
+ brandService.listBrands(filters).then((result) => result.length)
28
+ ]);
29
+ return res.json({
30
+ brands,
31
+ count,
32
+ offset: Number(offset),
33
+ limit: Number(limit),
34
+ });
35
+ }
36
+ catch (error) {
37
+ logger.error("Error listing brands:", error);
38
+ return res.status(500).json({ error: "Failed to list brands" });
39
+ }
40
+ };
41
+ exports.GET = GET;
42
+ // POST - Create a new brand
43
+ const POST = async (req, res) => {
44
+ const brandService = req.scope.resolve("brandCustom");
45
+ const logger = req.scope.resolve(utils_1.ContainerRegistrationKeys.LOGGER);
46
+ try {
47
+ const { name, slug, description, website, is_active = true, metadata, } = req.body;
48
+ if (!name || !slug) {
49
+ return res.status(400).json({
50
+ error: "Name and slug are required"
51
+ });
52
+ }
53
+ // Check if slug already exists
54
+ const existingBrand = await brandService.listBrands({ slug }).then((result) => result[0]);
55
+ if (existingBrand) {
56
+ return res.status(400).json({
57
+ error: "Brand with this slug already exists"
58
+ });
59
+ }
60
+ const brand = await brandService.createBrands([{
61
+ name,
62
+ slug,
63
+ description,
64
+ website,
65
+ is_active,
66
+ metadata,
67
+ }]).then((result) => result[0]);
68
+ return res.status(201).json({ brand });
69
+ }
70
+ catch (error) {
71
+ logger.error("Error creating brand:", error);
72
+ return res.status(500).json({ error: "Failed to create brand" });
73
+ }
74
+ };
75
+ exports.POST = POST;
@@ -0,0 +1,5 @@
1
+ import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http";
2
+ import { SetBrandRequest } from "../../../../../types";
3
+ export declare const GET: (req: MedusaRequest, res: MedusaResponse) => Promise<MedusaResponse>;
4
+ export declare const POST: (req: MedusaRequest<SetBrandRequest>, res: MedusaResponse) => Promise<MedusaResponse>;
5
+ export declare const DELETE: (req: MedusaRequest, res: MedusaResponse) => Promise<MedusaResponse>;
@@ -0,0 +1,116 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DELETE = exports.POST = exports.GET = void 0;
4
+ const utils_1 = require("@medusajs/framework/utils");
5
+ // GET - Get brand for a product
6
+ const GET = async (req, res) => {
7
+ const { id } = req.params;
8
+ const brandService = req.scope.resolve("brandCustom");
9
+ const productModuleService = req.scope.resolve(utils_1.Modules.PRODUCT);
10
+ const logger = req.scope.resolve(utils_1.ContainerRegistrationKeys.LOGGER);
11
+ try {
12
+ // Verify product exists
13
+ const product = await productModuleService.retrieveProduct(id);
14
+ if (!product) {
15
+ return res.status(404).json({ error: "Product not found" });
16
+ }
17
+ // Get product-brand association
18
+ const productBrand = await brandService.listProductBrands({
19
+ product_id: id,
20
+ }).then((result) => result[0]);
21
+ if (!productBrand) {
22
+ return res.json({ brand: null });
23
+ }
24
+ // Get brand details
25
+ const brand = await brandService.retrieveBrand(productBrand.brand_id);
26
+ return res.json({ brand });
27
+ }
28
+ catch (error) {
29
+ logger.error(`Error getting brand for product ${id}:`, error);
30
+ return res.status(500).json({ error: "Failed to get product brand" });
31
+ }
32
+ };
33
+ exports.GET = GET;
34
+ // POST - Assign a brand to a product
35
+ const POST = async (req, res) => {
36
+ const { id } = req.params;
37
+ const { brand_id } = req.body;
38
+ const brandService = req.scope.resolve("brandCustom");
39
+ const productModuleService = req.scope.resolve(utils_1.Modules.PRODUCT);
40
+ const logger = req.scope.resolve(utils_1.ContainerRegistrationKeys.LOGGER);
41
+ try {
42
+ if (!brand_id) {
43
+ return res.status(400).json({ error: "brand_id is required" });
44
+ }
45
+ // Verify product exists
46
+ const product = await productModuleService.retrieveProduct(id);
47
+ if (!product) {
48
+ return res.status(404).json({ error: "Product not found" });
49
+ }
50
+ // Verify brand exists
51
+ const brand = await brandService.retrieveBrand(brand_id);
52
+ if (!brand) {
53
+ return res.status(404).json({ error: "Brand not found" });
54
+ }
55
+ // Check if product already has a brand
56
+ const existingProductBrand = await brandService.listProductBrands({
57
+ product_id: id,
58
+ }).then((result) => result[0]);
59
+ if (existingProductBrand) {
60
+ // Update existing association
61
+ await brandService.updateProductBrands([{
62
+ id: existingProductBrand.id,
63
+ brand_id: brand_id,
64
+ }]);
65
+ }
66
+ else {
67
+ // Create new association
68
+ await brandService.createProductBrands([{
69
+ product_id: id,
70
+ brand_id: brand_id,
71
+ }]);
72
+ }
73
+ return res.json({
74
+ message: "Brand assigned successfully",
75
+ product_id: id,
76
+ brand_id: brand_id,
77
+ });
78
+ }
79
+ catch (error) {
80
+ logger.error(`Error assigning brand to product ${id}:`, error);
81
+ return res.status(500).json({ error: "Failed to assign brand to product" });
82
+ }
83
+ };
84
+ exports.POST = POST;
85
+ // DELETE - Remove brand from a product
86
+ const DELETE = async (req, res) => {
87
+ const { id } = req.params;
88
+ const brandService = req.scope.resolve("brandCustom");
89
+ const productModuleService = req.scope.resolve(utils_1.Modules.PRODUCT);
90
+ const logger = req.scope.resolve(utils_1.ContainerRegistrationKeys.LOGGER);
91
+ try {
92
+ // Verify product exists
93
+ const product = await productModuleService.retrieveProduct(id);
94
+ if (!product) {
95
+ return res.status(404).json({ error: "Product not found" });
96
+ }
97
+ // Get product-brand association
98
+ const productBrand = await brandService.listProductBrands({
99
+ product_id: id,
100
+ }).then((result) => result[0]);
101
+ if (!productBrand) {
102
+ return res.status(404).json({ error: "Product has no brand assigned" });
103
+ }
104
+ // Delete the association
105
+ await brandService.deleteProductBrands([productBrand.id]);
106
+ return res.json({
107
+ message: "Brand removed from product successfully",
108
+ product_id: id,
109
+ });
110
+ }
111
+ catch (error) {
112
+ logger.error(`Error removing brand from product ${id}:`, error);
113
+ return res.status(500).json({ error: "Failed to remove brand from product" });
114
+ }
115
+ };
116
+ exports.DELETE = DELETE;
@@ -0,0 +1,2 @@
1
+ import { MedusaRequest, MedusaResponse, MedusaNextFunction } from "@medusajs/framework/http";
2
+ export declare function attachBrandToProducts(req: MedusaRequest, res: MedusaResponse, next: MedusaNextFunction): Promise<void>;
@@ -0,0 +1,104 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.attachBrandToProducts = attachBrandToProducts;
4
+ const utils_1 = require("@medusajs/framework/utils");
5
+ // Middleware to attach brand information to product responses
6
+ async function attachBrandToProducts(req, res, next) {
7
+ // Store original json method
8
+ const originalJson = res.json.bind(res);
9
+ // Override json method to inject brand data
10
+ res.json = function (data) {
11
+ const brandService = req.scope.resolve("brandCustom");
12
+ const logger = req.scope.resolve(utils_1.ContainerRegistrationKeys.LOGGER);
13
+ // Process asynchronously and then call original json
14
+ (async () => {
15
+ try {
16
+ // Handle single product response (admin and store)
17
+ if (data?.product?.id) {
18
+ try {
19
+ // Get product-brand association
20
+ const productBrand = await brandService.listProductBrands({
21
+ product_id: data.product.id,
22
+ }).then((result) => result[0]);
23
+ if (productBrand) {
24
+ // Get brand details
25
+ const brand = await brandService.retrieveBrand(productBrand.brand_id);
26
+ data.product.brand = brand;
27
+ }
28
+ else {
29
+ data.product.brand = null;
30
+ }
31
+ }
32
+ catch (error) {
33
+ logger.error(`Error attaching brand to product ${data.product.id}:`, error);
34
+ data.product.brand = null;
35
+ }
36
+ }
37
+ // Handle multiple products response
38
+ if (data?.products && Array.isArray(data.products)) {
39
+ const productIds = data.products.map((p) => p.id);
40
+ try {
41
+ // Get all product-brand associations for these products
42
+ const productBrands = await brandService.listProductBrands({
43
+ product_id: { $in: productIds },
44
+ });
45
+ // Create a map for quick lookup
46
+ const brandMap = new Map();
47
+ const productBrandMap = new Map();
48
+ // Map product brands for quick lookup
49
+ productBrands.forEach((pb) => {
50
+ productBrandMap.set(pb.product_id, pb.brand_id);
51
+ });
52
+ // Fetch all unique brand IDs
53
+ const brandIds = [...new Set(productBrands.map((pb) => pb.brand_id))];
54
+ if (brandIds.length > 0) {
55
+ // Fetch all brands at once
56
+ const brands = await Promise.all(brandIds.map((id) => brandService.retrieveBrand(id).catch(() => null)));
57
+ // Map brands by ID
58
+ brands.forEach((brand) => {
59
+ if (brand) {
60
+ brandMap.set(brand.id, brand);
61
+ }
62
+ });
63
+ // Attach brands to products
64
+ data.products = data.products.map((product) => {
65
+ const brandId = productBrandMap.get(product.id);
66
+ if (brandId && brandMap.has(brandId)) {
67
+ product.brand = brandMap.get(brandId);
68
+ }
69
+ else {
70
+ product.brand = null;
71
+ }
72
+ return product;
73
+ });
74
+ }
75
+ else {
76
+ // No brands found, set all to null
77
+ data.products = data.products.map((product) => {
78
+ product.brand = null;
79
+ return product;
80
+ });
81
+ }
82
+ }
83
+ catch (error) {
84
+ logger.error("Error attaching brands to products:", error);
85
+ // On error, set all brands to null
86
+ data.products = data.products.map((product) => {
87
+ product.brand = null;
88
+ return product;
89
+ });
90
+ }
91
+ }
92
+ }
93
+ catch (error) {
94
+ // Silently fail and continue without brand data
95
+ console.error("Brand middleware error:", error);
96
+ }
97
+ // Call original json method with potentially modified data
98
+ originalJson(data);
99
+ })();
100
+ // Return the response object synchronously
101
+ return res;
102
+ };
103
+ next();
104
+ }
@@ -0,0 +1,6 @@
1
+ import { attachBrandToProducts } from "./middlewares/attach-brand-to-products";
2
+ export declare const config: {
3
+ method: string[];
4
+ matcher: string;
5
+ middlewares: (typeof attachBrandToProducts)[];
6
+ }[];
@@ -0,0 +1,26 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.config = void 0;
4
+ const attach_brand_to_products_1 = require("./middlewares/attach-brand-to-products");
5
+ exports.config = [
6
+ {
7
+ method: ["GET"],
8
+ matcher: "/admin/products",
9
+ middlewares: [attach_brand_to_products_1.attachBrandToProducts],
10
+ },
11
+ {
12
+ method: ["GET"],
13
+ matcher: "/admin/products/:id",
14
+ middlewares: [attach_brand_to_products_1.attachBrandToProducts],
15
+ },
16
+ {
17
+ method: ["GET"],
18
+ matcher: "/store/products",
19
+ middlewares: [attach_brand_to_products_1.attachBrandToProducts],
20
+ },
21
+ {
22
+ method: ["GET"],
23
+ matcher: "/store/products/:id",
24
+ middlewares: [attach_brand_to_products_1.attachBrandToProducts],
25
+ },
26
+ ];
@@ -0,0 +1,2 @@
1
+ import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http";
2
+ export declare const GET: (req: MedusaRequest, res: MedusaResponse) => Promise<MedusaResponse>;