@lodashventure/medusa-hero 1.0.1

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.
@@ -0,0 +1,95 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.deleteHeroesWorkflow = void 0;
4
+ const workflows_sdk_1 = require("@medusajs/framework/workflows-sdk");
5
+ const core_flows_1 = require("@medusajs/medusa/core-flows");
6
+ const hero_1 = require("../modules/hero");
7
+ const utils_1 = require("@medusajs/framework/utils");
8
+ const deleteHeroesStep = (0, workflows_sdk_1.createStep)("delete-heroes", async ({ ids }, { container }) => {
9
+ const logger = container.resolve("logger");
10
+ const heroModuleService = container.resolve(hero_1.HERO_MODULE);
11
+ if (!ids.length) {
12
+ throw new Error("At least one hero ID is required");
13
+ }
14
+ logger.info(`Deleting ${ids.length} heroes`);
15
+ // First, fetch the heroes to ensure they exist
16
+ const heroes = await heroModuleService.listHeroes({
17
+ id: ids,
18
+ });
19
+ if (!heroes.length) {
20
+ throw new Error("No heroes found with the provided IDs");
21
+ }
22
+ const heroIds = heroes.map((hero) => hero.id);
23
+ // Try to delete files using the file service
24
+ // We'll handle file deletion errors gracefully since the files might already be deleted
25
+ try {
26
+ const fileService = container.resolve(utils_1.Modules.FILE);
27
+ // Extract filenames from hero URLs and create proper file objects
28
+ const fileKeysToDelete = [];
29
+ for (const hero of heroes) {
30
+ if (hero.url) {
31
+ // Extract filename from URL like https://storage.googleapis.com/sangaroon/filename.jpg
32
+ const urlParts = hero.url.split('/');
33
+ const filename = urlParts[urlParts.length - 1];
34
+ if (filename) {
35
+ logger.info(`Preparing to delete file: ${filename} from URL: ${hero.url}`);
36
+ fileKeysToDelete.push(filename);
37
+ }
38
+ }
39
+ }
40
+ if (fileKeysToDelete.length > 0) {
41
+ logger.info(`Attempting to delete ${fileKeysToDelete.length} files from storage`);
42
+ try {
43
+ // Try to delete files using the deleteFiles method
44
+ // First try with filenames as IDs
45
+ try {
46
+ await fileService.deleteFiles(fileKeysToDelete);
47
+ logger.info(`Successfully deleted files using filenames`);
48
+ }
49
+ catch (deleteError) {
50
+ logger.warn(`Could not delete using filenames: ${deleteError.message}`);
51
+ // Try with hero IDs instead
52
+ try {
53
+ await fileService.deleteFiles(heroIds);
54
+ logger.info(`Successfully deleted files using hero IDs`);
55
+ }
56
+ catch (fallbackError) {
57
+ logger.error(`Could not delete files using hero IDs either: ${fallbackError.message}`);
58
+ // Last attempt: use deleteFilesWorkflow with hero IDs
59
+ try {
60
+ await (0, core_flows_1.deleteFilesWorkflow)(container).run({
61
+ input: { ids: heroIds },
62
+ });
63
+ logger.info(`Successfully deleted files using deleteFilesWorkflow`);
64
+ }
65
+ catch (workflowError) {
66
+ logger.error(`Error deleting files with workflow: ${workflowError.message}`);
67
+ logger.info(`Files may remain in storage, but continuing with hero deletion`);
68
+ }
69
+ }
70
+ }
71
+ }
72
+ catch (fileDeleteError) {
73
+ // Log the error but continue with hero deletion
74
+ logger.error(`Error in file deletion process: ${fileDeleteError.message}`);
75
+ logger.info(`Continuing with hero record deletion despite file deletion error`);
76
+ }
77
+ }
78
+ else {
79
+ logger.info(`No files to delete from storage`);
80
+ }
81
+ }
82
+ catch (error) {
83
+ // If file service is not available or any other error, log and continue
84
+ logger.error(`Error accessing file service: ${error.message}`);
85
+ logger.info(`Continuing with hero record deletion despite file service error`);
86
+ }
87
+ // Delete the hero records regardless of file deletion success
88
+ await heroModuleService.deleteHeroes(heroIds);
89
+ return new workflows_sdk_1.StepResponse(heroIds, heroIds);
90
+ });
91
+ exports.deleteHeroesWorkflow = (0, workflows_sdk_1.createWorkflow)("delete-heroes", (deleteInput) => {
92
+ const post = deleteHeroesStep(deleteInput);
93
+ return new workflows_sdk_1.WorkflowResponse(post);
94
+ });
95
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZGVsZXRlLWhlcm9lcy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3NyYy93b3JrZmxvd3MvZGVsZXRlLWhlcm9lcy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFBQSxxRUFLMkM7QUFDM0MsNERBQWtFO0FBRWxFLDBDQUE4QztBQUM5QyxxREFBb0Q7QUFNcEQsTUFBTSxnQkFBZ0IsR0FBRyxJQUFBLDBCQUFVLEVBQ2pDLGVBQWUsRUFDZixLQUFLLEVBQUUsRUFBRSxHQUFHLEVBQTZCLEVBQUUsRUFBRSxTQUFTLEVBQUUsRUFBRSxFQUFFO0lBQzFELE1BQU0sTUFBTSxHQUFHLFNBQVMsQ0FBQyxPQUFPLENBQUMsUUFBUSxDQUFDLENBQUM7SUFDM0MsTUFBTSxpQkFBaUIsR0FDckIsU0FBUyxDQUFDLE9BQU8sQ0FBQyxrQkFBVyxDQUFDLENBQUM7SUFFakMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxNQUFNLEVBQUUsQ0FBQztRQUNoQixNQUFNLElBQUksS0FBSyxDQUFDLGtDQUFrQyxDQUFDLENBQUM7SUFDdEQsQ0FBQztJQUVELE1BQU0sQ0FBQyxJQUFJLENBQUMsWUFBWSxHQUFHLENBQUMsTUFBTSxTQUFTLENBQUMsQ0FBQztJQUU3QywrQ0FBK0M7SUFDL0MsTUFBTSxNQUFNLEdBQUcsTUFBTSxpQkFBaUIsQ0FBQyxVQUFVLENBQUM7UUFDaEQsRUFBRSxFQUFFLEdBQUc7S0FDUixDQUFDLENBQUM7SUFFSCxJQUFJLENBQUMsTUFBTSxDQUFDLE1BQU0sRUFBRSxDQUFDO1FBQ25CLE1BQU0sSUFBSSxLQUFLLENBQUMsdUNBQXVDLENBQUMsQ0FBQztJQUMzRCxDQUFDO0lBRUQsTUFBTSxPQUFPLEdBQUcsTUFBTSxDQUFDLEdBQUcsQ0FBQyxDQUFDLElBQUksRUFBRSxFQUFFLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FBQyxDQUFDO0lBRTlDLDZDQUE2QztJQUM3Qyx3RkFBd0Y7SUFDeEYsSUFBSSxDQUFDO1FBQ0gsTUFBTSxX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
@@ -0,0 +1,23 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.reorderHeroesWorkflow = void 0;
4
+ const workflows_sdk_1 = require("@medusajs/framework/workflows-sdk");
5
+ const hero_1 = require("../modules/hero");
6
+ const reorderHeroesStep = (0, workflows_sdk_1.createStep)("reorder-heroes", async ({ ids }, { container }) => {
7
+ const logger = container.resolve("logger");
8
+ const heroModuleService = container.resolve(hero_1.HERO_MODULE);
9
+ if (!ids.length) {
10
+ throw new Error("At least one hero ID is required");
11
+ }
12
+ const heroes = await heroModuleService.listHeroes();
13
+ if (heroes.length !== ids.length) {
14
+ throw new Error("Hero IDs and heroes length does not match");
15
+ }
16
+ await heroModuleService.updateHeroes(ids.map((id, index) => ({ id, order: ids.length - index })));
17
+ return new workflows_sdk_1.StepResponse(ids, ids);
18
+ });
19
+ exports.reorderHeroesWorkflow = (0, workflows_sdk_1.createWorkflow)("reorder-heroes", (reorderInput) => {
20
+ const post = reorderHeroesStep(reorderInput);
21
+ return new workflows_sdk_1.WorkflowResponse(post);
22
+ });
23
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicmVvcmRlci1oZXJvZXMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9zcmMvd29ya2Zsb3dzL3Jlb3JkZXItaGVyb2VzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQUFBLHFFQUsyQztBQUMzQywwQ0FBOEM7QUFROUMsTUFBTSxpQkFBaUIsR0FBRyxJQUFBLDBCQUFVLEVBQ2xDLGdCQUFnQixFQUNoQixLQUFLLEVBQUUsRUFBRSxHQUFHLEVBQThCLEVBQUUsRUFBRSxTQUFTLEVBQUUsRUFBRSxFQUFFO0lBQzNELE1BQU0sTUFBTSxHQUFHLFNBQVMsQ0FBQyxPQUFPLENBQUMsUUFBUSxDQUFDLENBQUM7SUFDM0MsTUFBTSxpQkFBaUIsR0FDckIsU0FBUyxDQUFDLE9BQU8sQ0FBQyxrQkFBVyxDQUFDLENBQUM7SUFFakMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxNQUFNLEVBQUUsQ0FBQztRQUNoQixNQUFNLElBQUksS0FBSyxDQUFDLGtDQUFrQyxDQUFDLENBQUM7SUFDdEQsQ0FBQztJQUVELE1BQU0sTUFBTSxHQUFHLE1BQU0saUJBQWlCLENBQUMsVUFBVSxFQUFFLENBQUM7SUFDcEQsSUFBSSxNQUFNLENBQUMsTUFBTSxLQUFLLEdBQUcsQ0FBQyxNQUFNLEVBQUUsQ0FBQztRQUNqQyxNQUFNLElBQUksS0FBSyxDQUFDLDJDQUEyQyxDQUFDLENBQUM7SUFDL0QsQ0FBQztJQUVELE1BQU0saUJBQWlCLENBQUMsWUFBWSxDQUNsQyxHQUFHLENBQUMsR0FBRyxDQUFDLENBQUMsRUFBRSxFQUFFLEtBQUssRUFBRSxFQUFFLENBQUMsQ0FBQyxFQUFFLEVBQUUsRUFBRSxLQUFLLEVBQUUsR0FBRyxDQUFDLE1BQU0sR0FBRyxLQUFLLEVBQUUsQ0FBQyxDQUFDLENBQzVELENBQUM7SUFFRixPQUFPLElBQUksNEJBQVksQ0FBQyxHQUFHLEVBQUUsR0FBRyxDQUFDLENBQUM7QUFDcEMsQ0FBQyxDQUNGLENBQUM7QUFFVyxRQUFBLHFCQUFxQixHQUFHLElBQUEsOEJBQWMsRUFDakQsZ0JBQWdCLEVBQ2hCLENBQUMsWUFBd0MsRUFBRSxFQUFFO0lBQzNDLE1BQU0sSUFBSSxHQUFHLGlCQUFpQixDQUFDLFlBQVksQ0FBQyxDQUFDO0lBQzdDLE9BQU8sSUFBSSxnQ0FBZ0IsQ0FBQyxJQUFJLENBQUMsQ0FBQztBQUNwQyxDQUFDLENBQ0YsQ0FBQyJ9
@@ -0,0 +1,64 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.uploadCategoryImageWorkflow = void 0;
4
+ const workflows_sdk_1 = require("@medusajs/framework/workflows-sdk");
5
+ const gcs_direct_upload_1 = require("../services/gcs-direct-upload");
6
+ const utils_1 = require("@medusajs/framework/utils");
7
+ const uploadCategoryImageStep = (0, workflows_sdk_1.createStep)("upload-category-image-step", async ({ categoryId, imageType, fileData }, { container }) => {
8
+ const query = container.resolve(utils_1.ContainerRegistrationKeys.QUERY);
9
+ const gcsUploadService = new gcs_direct_upload_1.GcsDirectUploadService();
10
+ // Generate unique filename for direct upload
11
+ const timestamp = Date.now();
12
+ const ext = fileData.filename.split('.').pop() || 'jpg';
13
+ const filename = `categories/${imageType}-${categoryId}-${timestamp}.${ext}`;
14
+ // Convert base64 to buffer
15
+ const buffer = Buffer.from(fileData.content, "base64");
16
+ // Upload directly to GCS
17
+ const publicUrl = await gcsUploadService.uploadFile(filename, buffer, fileData.mimeType);
18
+ // Retrieve existing category
19
+ const { data: categories } = await query.graph({
20
+ entity: "product_category",
21
+ fields: ["id", "metadata"],
22
+ filters: { id: categoryId },
23
+ });
24
+ if (!categories || categories.length === 0) {
25
+ throw new Error(`Category with id ${categoryId} not found`);
26
+ }
27
+ const category = categories[0];
28
+ // Delete old image if it exists
29
+ const oldImageUrl = category.metadata?.[imageType];
30
+ if (oldImageUrl) {
31
+ try {
32
+ if (oldImageUrl.includes('storage.googleapis.com')) {
33
+ const parts = oldImageUrl.split('/');
34
+ const bucketIndex = parts.indexOf('sangaroon');
35
+ if (bucketIndex !== -1 && bucketIndex < parts.length - 1) {
36
+ const oldFilename = parts.slice(bucketIndex + 1).join('/');
37
+ await gcsUploadService.deleteFile(oldFilename);
38
+ }
39
+ }
40
+ }
41
+ catch (error) {
42
+ console.error(`Failed to delete old ${imageType}:`, error);
43
+ // Continue even if deletion fails
44
+ }
45
+ }
46
+ // Update category metadata with new image URL
47
+ const updatedMetadata = {
48
+ ...category.metadata,
49
+ [imageType]: publicUrl,
50
+ };
51
+ // Note: Category metadata update should be done via the category service
52
+ // For now, we'll return the URL and let the route handler update it
53
+ return new workflows_sdk_1.StepResponse({
54
+ categoryId,
55
+ imageType,
56
+ imageUrl: publicUrl,
57
+ metadata: updatedMetadata,
58
+ });
59
+ });
60
+ exports.uploadCategoryImageWorkflow = (0, workflows_sdk_1.createWorkflow)("upload-category-image", (input) => {
61
+ const result = uploadCategoryImageStep(input);
62
+ return new workflows_sdk_1.WorkflowResponse(result);
63
+ });
64
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidXBsb2FkLWNhdGVnb3J5LWltYWdlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vc3JjL3dvcmtmbG93cy91cGxvYWQtY2F0ZWdvcnktaW1hZ2UudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBQUEscUVBTTJDO0FBQzNDLHFFQUF1RTtBQUN2RSxxREFBc0U7QUFZdEUsTUFBTSx1QkFBdUIsR0FBRyxJQUFBLDBCQUFVLEVBQ3hDLDRCQUE0QixFQUM1QixLQUFLLEVBQUUsRUFBRSxVQUFVLEVBQUUsU0FBUyxFQUFFLFFBQVEsRUFBNEIsRUFBRSxFQUFFLFNBQVMsRUFBRSxFQUFFLEVBQUU7SUFDckYsTUFBTSxLQUFLLEdBQUcsU0FBUyxDQUFDLE9BQU8sQ0FBQyxpQ0FBeUIsQ0FBQyxLQUFLLENBQUMsQ0FBQztJQUNqRSxNQUFNLGdCQUFnQixHQUFHLElBQUksMENBQXNCLEVBQUUsQ0FBQztJQUV0RCw2Q0FBNkM7SUFDN0MsTUFBTSxTQUFTLEdBQUcsSUFBSSxDQUFDLEdBQUcsRUFBRSxDQUFDO0lBQzdCLE1BQU0sR0FBRyxHQUFHLFFBQVEsQ0FBQyxRQUFRLENBQUMsS0FBSyxDQUFDLEdBQUcsQ0FBQyxDQUFDLEdBQUcsRUFBRSxJQUFJLEtBQUssQ0FBQztJQUN4RCxNQUFNLFFBQVEsR0FBRyxjQUFjLFNBQVMsSUFBSSxVQUFVLElBQUksU0FBUyxJQUFJLEdBQUcsRUFBRSxDQUFDO0lBRTdFLDJCQUEyQjtJQUMzQixNQUFNLE1BQU0sR0FBRyxNQUFNLENBQUMsSUFBSSxDQUFDLFFBQVEsQ0FBQyxPQUFPLEVBQUUsUUFBUSxDQUFDLENBQUM7SUFFdkQseUJBQXlCO0lBQ3pCLE1BQU0sU0FBUyxHQUFHLE1BQU0sZ0JBQWdCLENBQUMsVUFBVSxDQUFDLFFBQVEsRUFBRSxNQUFNLEVBQUUsUUFBUSxDQUFDLFFBQVEsQ0FBQyxDQUFDO0lBRXpGLDZCQUE2QjtJQUM3QixNQUFNLEVBQUUsSUFBSSxFQUFFLFVBQVUsRUFBRSxHQUFHLE1BQU0sS0FBSyxDQUFDLEtBQUssQ0FBQztRQUM3QyxNQUFNLEVBQUUsa0JBQWtCO1FBQzFCLE1BQU0sRUFBRSxDQUFDLElBQUksRUFBRSxVQUFVLENBQUM7UUFDMUIsT0FBTyxFQUFFLEVBQUUsRUFBRSxFQUFFLFVBQVUsRUFBRTtLQUM1QixDQUFDLENBQUM7SUFFSCxJQUFJLENBQUMsVUFBVSxJQUFJLFVBQVUsQ0FBQyxNQUFNLEtBQUssQ0FBQyxFQUFFLENBQUM7UUFDM0MsTUFBTSxJQUFJLEtBQUssQ0FBQyxvQkFBb0IsVUFBVSxZQUFZLENBQUMsQ0FBQztJQUM5RCxDQUFDO0lBRUQsTUFBTSxRQUFRLEdBQUcsVUFBVSxDQUFDLENBQUMsQ0FBQyxDQUFDO0lBRS9CLGdDQUFnQztJQUNoQyxNQUFNLFdBQVcsR0FBRyxRQUFRLENBQUMsUUFBUSxFQUFFLENBQUMsU0FBUyxDQUFDLENBQUM7SUFDbkQsSUFBSSxXQUFXLEVBQUUsQ0FBQztRQUNoQixJQUFJLENBQUM7WUFDSCxJQUFJLFdBQVcsQ0FBQyxRQUFRLENBQUMsd0JBQXdCLENBQUMsRUFBRSxDQUFDO2dCQUNuRCxNQUFNLEtBQUssR0FBRyxXQUFXLENBQUMsS0FBSyxDQUFDLEdBQUcsQ0FBQyxDQUFDO2dCQUNyQyxNQUFNLFdBQVcsR0FBRyxLQUFLLENBQUMsT0FBTyxDQUFDLFdBQVcsQ0FBQyxDQUFDO2dCQUMvQyxJQUFJLFdBQVcsS0FBSyxDQUFDLENBQUMsSUFBSSxXQUFXLEdBQUcsS0FBSyxDQUFDLE1BQU0sR0FBRyxDQUFDLEVBQUUsQ0FBQztvQkFDekQsTUFBTSxXQUFXLEdBQUcsS0FBSyxDQUFDLEtBQUssQ0FBQyxXQUFXLEdBQUcsQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxDQUFDO29CQUMzRCxNQUFNLGdCQUFnQixDQUFDLFVBQVUsQ0FBQyxXQUFXLENBQUMsQ0FBQztnQkFDakQsQ0FBQztZQUNILENBQUM7UUFDSCxDQUFDO1FBQUMsT0FBTyxLQUFLLEVBQUUsQ0FBQztZQUNmLE9BQU8sQ0FBQyxLQUFLLENBQUMsd0JBQXdCLFNBQVMsR0FBRyxFQUFFLEtBQUssQ0FBQyxDQUFDO1lBQzNELGtDQUFrQztRQUNwQyxDQUFDO0lBQ0gsQ0FBQztJQUVELDhDQUE4QztJQUM5QyxNQUFNLGVBQWUsR0FBRztRQUN0QixHQUFHLFFBQVEsQ0FBQyxRQUFRO1FBQ3BCLENBQUMsU0FBUyxDQUFDLEVBQUUsU0FBUztLQUN2QixDQUFDO0lBRUYseUVBQXlFO0lBQ3pFLG9FQUFvRTtJQUVwRSxPQUFPLElBQUksNEJBQVksQ0FBQztRQUN0QixVQUFVO1FBQ1YsU0FBUztRQUNULFFBQVEsRUFBRSxTQUFTO1FBQ25CLFFBQVEsRUFBRSxlQUFlO0tBQzFCLENBQUMsQ0FBQztBQUNMLENBQUMsQ0FDRixDQUFDO0FBRVcsUUFBQSwyQkFBMkIsR0FBRyxJQUFBLDhCQUFjLEVBQ3ZELHVCQUF1QixFQUN2QixDQUFDLEtBQTZDLEVBQUUsRUFBRTtJQUNoRCxNQUFNLE1BQU0sR0FBRyx1QkFBdUIsQ0FBQyxLQUFLLENBQUMsQ0FBQztJQUM5QyxPQUFPLElBQUksZ0NBQWdCLENBQUMsTUFBTSxDQUFDLENBQUM7QUFDdEMsQ0FBQyxDQUNGLENBQUMifQ==
@@ -0,0 +1,54 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.uploadHeroesWorkflow = void 0;
4
+ const workflows_sdk_1 = require("@medusajs/framework/workflows-sdk");
5
+ const hero_1 = require("../modules/hero");
6
+ const core_flows_1 = require("@medusajs/medusa/core-flows");
7
+ const uploadHeroesStep = (0, workflows_sdk_1.createStep)("upload-heroes", async ({ files }, { container }) => {
8
+ const logger = container.resolve("logger");
9
+ const heroModuleService = container.resolve(hero_1.HERO_MODULE);
10
+ if (files.length === 0) {
11
+ throw new Error("at least 1 file is required");
12
+ }
13
+ logger.info(`Uploading ${files.length} heroes`);
14
+ const { result } = await (0, core_flows_1.uploadFilesWorkflow)(container).run({
15
+ input: {
16
+ files: files.map((file) => ({
17
+ ...file,
18
+ filename: `${file.filename.replace(/ /g, "_")}`,
19
+ })),
20
+ },
21
+ });
22
+ if (result.some((file) => !file.id || !file.url)) {
23
+ throw new Error(`some files don't have an id or url`);
24
+ }
25
+ const [maxOrderHero] = await heroModuleService.listHeroes({}, { take: 1, order: { order: "DESC" } });
26
+ const maxOrder = maxOrderHero?.order ?? 0;
27
+ const heroes = await heroModuleService.createHeroes(result.map((hero, index) => ({
28
+ id: hero.id,
29
+ url: hero.url,
30
+ order: maxOrder + index + 1,
31
+ })));
32
+ return new workflows_sdk_1.StepResponse(heroes, heroes);
33
+ }, async (heroes, { container }) => {
34
+ const logger = container.resolve("logger");
35
+ const heroModuleService = container.resolve(hero_1.HERO_MODULE);
36
+ if (heroes) {
37
+ // delete files
38
+ await (0, core_flows_1.deleteFilesWorkflow)(container).run({
39
+ input: {
40
+ ids: heroes.map((hero) => hero.id),
41
+ },
42
+ });
43
+ // delete heroes
44
+ await heroModuleService.deleteHeroes(heroes.map((hero) => hero.id));
45
+ }
46
+ else {
47
+ logger.warn(`Hero not found`);
48
+ }
49
+ });
50
+ exports.uploadHeroesWorkflow = (0, workflows_sdk_1.createWorkflow)("upload-heroes", (heroInput) => {
51
+ const post = uploadHeroesStep(heroInput);
52
+ return new workflows_sdk_1.WorkflowResponse(post);
53
+ });
54
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidXBsb2FkLWhlcm9lcy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3NyYy93b3JrZmxvd3MvdXBsb2FkLWhlcm9lcy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFBQSxxRUFLMkM7QUFDM0MsMENBQThDO0FBRTlDLDREQUlxQztBQU1yQyxNQUFNLGdCQUFnQixHQUFHLElBQUEsMEJBQVUsRUFDakMsZUFBZSxFQUNmLEtBQUssRUFBRSxFQUFFLEtBQUssRUFBNkIsRUFBRSxFQUFFLFNBQVMsRUFBRSxFQUFFLEVBQUU7SUFDNUQsTUFBTSxNQUFNLEdBQUcsU0FBUyxDQUFDLE9BQU8sQ0FBQyxRQUFRLENBQUMsQ0FBQztJQUMzQyxNQUFNLGlCQUFpQixHQUNyQixTQUFTLENBQUMsT0FBTyxDQUFDLGtCQUFXLENBQUMsQ0FBQztJQUVqQyxJQUFJLEtBQUssQ0FBQyxNQUFNLEtBQUssQ0FBQyxFQUFFLENBQUM7UUFDdkIsTUFBTSxJQUFJLEtBQUssQ0FBQyw2QkFBNkIsQ0FBQyxDQUFDO0lBQ2pELENBQUM7SUFFRCxNQUFNLENBQUMsSUFBSSxDQUFDLGFBQWEsS0FBSyxDQUFDLE1BQU0sU0FBUyxDQUFDLENBQUM7SUFFaEQsTUFBTSxFQUFFLE1BQU0sRUFBRSxHQUFHLE1BQU0sSUFBQSxnQ0FBbUIsRUFBQyxTQUFTLENBQUMsQ0FBQyxHQUFHLENBQUM7UUFDMUQsS0FBSyxFQUFFO1lBQ0wsS0FBSyxFQUFFLEtBQUssQ0FBQyxHQUFHLENBQUMsQ0FBQyxJQUFJLEVBQUUsRUFBRSxDQUFDLENBQUM7Z0JBQzFCLEdBQUcsSUFBSTtnQkFDUCxRQUFRLEVBQUUsR0FBRyxJQUFJLENBQUMsUUFBUSxDQUFDLE9BQU8sQ0FBQyxJQUFJLEVBQUUsR0FBRyxDQUFDLEVBQUU7YUFDaEQsQ0FBQyxDQUFDO1NBQ0o7S0FDRixDQUFDLENBQUM7SUFFSCxJQUFJLE1BQU0sQ0FBQyxJQUFJLENBQUMsQ0FBQyxJQUFJLEVBQUUsRUFBRSxDQUFDLENBQUMsSUFBSSxDQUFDLEVBQUUsSUFBSSxDQUFDLElBQUksQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDO1FBQ2pELE1BQU0sSUFBSSxLQUFLLENBQUMsb0NBQW9DLENBQUMsQ0FBQztJQUN4RCxDQUFDO0lBRUQsTUFBTSxDQUFDLFlBQVksQ0FBQyxHQUFHLE1BQU0saUJBQWlCLENBQUMsVUFBVSxDQUN2RCxFQUFFLEVBQ0YsRUFBRSxJQUFJLEVBQUUsQ0FBQyxFQUFFLEtBQUssRUFBRSxFQUFFLEtBQUssRUFBRSxNQUFNLEVBQUUsRUFBRSxDQUN0QyxDQUFDO0lBQ0YsTUFBTSxRQUFRLEdBQUcsWUFBWSxFQUFFLEtBQUssSUFBSSxDQUFDLENBQUM7SUFFMUMsTUFBTSxNQUFNLEdBQUcsTUFBTSxpQkFBaUIsQ0FBQyxZQUFZLENBQ2pELE1BQU0sQ0FBQyxHQUFHLENBQUMsQ0FBQyxJQUFJLEVBQUUsS0FBSyxFQUFFLEVBQUUsQ0FBQyxDQUFDO1FBQzNCLEVBQUUsRUFBRSxJQUFJLENBQUMsRUFBRTtRQUNYLEdBQUcsRUFBRSxJQUFJLENBQUMsR0FBRztRQUNiLEtBQUssRUFBRSxRQUFRLEdBQUcsS0FBSyxHQUFHLENBQUM7S0FDNUIsQ0FBQyxDQUFDLENBQ0osQ0FBQztJQUVGLE9BQU8sSUFBSSw0QkFBWSxDQUFDLE1BQU0sRUFBRSxNQUFNLENBQUMsQ0FBQztBQUMxQyxDQUFDLEVBRUQsS0FBSyxFQUFFLE1BQU0sRUFBRSxFQUFFLFNBQVMsRUFBRSxFQUFFLEVBQUU7SUFDOUIsTUFBTSxNQUFNLEdBQUcsU0FBUyxDQUFDLE9BQU8sQ0FBQyxRQUFRLENBQUMsQ0FBQztJQUMzQyxNQUFNLGlCQUFpQixHQUNyQixTQUFTLENBQUMsT0FBTyxDQUFDLGtCQUFXLENBQUMsQ0FBQztJQUVqQyxJQUFJLE1BQU0sRUFBRSxDQUFDO1FBQ1gsZUFBZTtRQUNmLE1BQU0sSUFBQSxnQ0FBbUIsRUFBQyxTQUFTLENBQUMsQ0FBQyxHQUFHLENBQUM7WUFDdkMsS0FBSyxFQUFFO2dCQUNMLEdBQUcsRUFBRSxNQUFNLENBQUMsR0FBRyxDQUFDLENBQUMsSUFBSSxFQUFFLEVBQUUsQ0FBQyxJQUFJLENBQUMsRUFBRSxDQUFDO2FBQ25DO1NBQ0YsQ0FBQyxDQUFDO1FBQ0gsZ0JBQWdCO1FBQ2hCLE1BQU0saUJBQWlCLENBQUMsWUFBWSxDQUNsQyxNQUFNLENBQUMsR0FBRyxDQUFDLENBQUMsSUFBSSxFQUFFLEVBQUUsQ0FBQyxJQUFJLENBQUMsRUFBRSxDQUFDLENBQzlCLENBQUM7SUFDSixDQUFDO1NBQU0sQ0FBQztRQUNOLE1BQU0sQ0FBQyxJQUFJLENBQUMsZ0JBQWdCLENBQUMsQ0FBQztJQUNoQyxDQUFDO0FBQ0gsQ0FBQyxDQUNGLENBQUM7QUFFVyxRQUFBLG9CQUFvQixHQUFHLElBQUEsOEJBQWMsRUFDaEQsZUFBZSxFQUNmLENBQUMsU0FBb0MsRUFBRSxFQUFFO0lBQ3ZDLE1BQU0sSUFBSSxHQUFHLGdCQUFnQixDQUFDLFNBQVMsQ0FBQyxDQUFDO0lBRXpDLE9BQU8sSUFBSSxnQ0FBZ0IsQ0FBQyxJQUFJLENBQUMsQ0FBQztBQUNwQyxDQUFDLENBQ0YsQ0FBQyJ9
package/README.md ADDED
@@ -0,0 +1,68 @@
1
+ # Medusa Hero - Category Media Plugin
2
+
3
+ A Medusa v2 plugin for managing category thumbnail and hero images with drag & drop upload functionality.
4
+
5
+ ## Features
6
+
7
+ - Upload category thumbnail images (400x400px recommended)
8
+ - Upload category hero/banner images (1920x600px recommended)
9
+ - Delete category images
10
+ - Drag & drop file upload interface
11
+ - Image preview in admin dashboard
12
+ - Direct upload to Google Cloud Storage
13
+ - Automatic old image cleanup
14
+
15
+ ## Installation
16
+
17
+ ```bash
18
+ cd backend/plugins/medusa-hero
19
+ npm install
20
+ npm run build
21
+ ```
22
+
23
+ ## Configuration
24
+
25
+ Add to your `medusa-config.ts`:
26
+
27
+ ```typescript
28
+ {
29
+ resolve: "@lodashventure/medusa-hero",
30
+ options: {},
31
+ }
32
+ ```
33
+
34
+ ## API Endpoints
35
+
36
+ ### Admin Endpoints
37
+
38
+ - `GET /admin/categories/:id/thumbnail` - Get category thumbnail
39
+ - `POST /admin/categories/:id/thumbnail` - Upload thumbnail (multipart/form-data)
40
+ - `DELETE /admin/categories/:id/thumbnail` - Delete thumbnail
41
+ - `GET /admin/categories/:id/hero` - Get category hero image
42
+ - `POST /admin/categories/:id/hero` - Upload hero image (multipart/form-data)
43
+ - `DELETE /admin/categories/:id/hero` - Delete hero image
44
+
45
+ ## Usage
46
+
47
+ The plugin automatically adds two widgets to the category detail page in the admin dashboard:
48
+
49
+ 1. **Category Thumbnail Widget** - For square category images
50
+ 2. **Category Hero Widget** - For wide banner images
51
+
52
+ Both widgets support:
53
+ - Drag & drop upload
54
+ - Click to browse
55
+ - Image preview
56
+ - Delete functionality
57
+ - File validation (type & size)
58
+
59
+ ## Requirements
60
+
61
+ - Medusa v2.11.0+
62
+ - Node.js 20+
63
+ - Google Cloud Storage configured
64
+ - Environment variables: `CLIENT_EMAIL`, `PRIVATE_KEY`, `BUCKET_NAME`
65
+
66
+ ## License
67
+
68
+ MIT
package/package.json ADDED
@@ -0,0 +1,67 @@
1
+ {
2
+ "name": "@lodashventure/medusa-hero",
3
+ "version": "1.0.1",
4
+ "description": "Plugin for managing category thumbnail and hero images in Medusa",
5
+ "author": "StandUpCode",
6
+ "license": "MIT",
7
+ "files": [
8
+ ".medusa/server"
9
+ ],
10
+ "exports": {
11
+ "./package.json": "./package.json",
12
+ "./workflows": "./.medusa/server/src/workflows/index.js",
13
+ "./.medusa/server/src/modules/*": "./.medusa/server/src/modules/*/index.js",
14
+ "./modules/*": "./.medusa/server/src/modules/*/index.js",
15
+ "./providers/*": "./.medusa/server/src/providers/*/index.js",
16
+ "./admin": "./.medusa/server/src/admin/index.mjs",
17
+ "./*": "./.medusa/server/src/*.js"
18
+ },
19
+ "keywords": [
20
+ "medusa",
21
+ "plugin",
22
+ "medusa-plugin-other",
23
+ "medusa-plugin",
24
+ "medusa-v2",
25
+ "category",
26
+ "images"
27
+ ],
28
+ "scripts": {
29
+ "build": "medusa plugin:build",
30
+ "dev": "medusa plugin:develop",
31
+ "prepublishOnly": "medusa plugin:build"
32
+ },
33
+ "devDependencies": {
34
+ "@medusajs/admin-sdk": "2.11.1",
35
+ "@medusajs/cli": "2.11.1",
36
+ "@medusajs/framework": "2.11.1",
37
+ "@medusajs/icons": "^2.11.1",
38
+ "@medusajs/medusa": "2.11.1",
39
+ "@medusajs/ui": "4.0.4",
40
+ "@swc/core": "1.5.7",
41
+ "@types/multer": "^1.4.12",
42
+ "@types/node": "^20.0.0",
43
+ "@types/react": "^18.3.2",
44
+ "@types/react-dom": "^18.2.25",
45
+ "react": "^18.2.0",
46
+ "react-dom": "^18.2.0",
47
+ "typescript": "^5.6.2"
48
+ },
49
+ "peerDependencies": {
50
+ "@medusajs/admin-sdk": "2.11.1",
51
+ "@medusajs/framework": "2.11.1",
52
+ "@medusajs/icons": "^2.11.1",
53
+ "@medusajs/medusa": "2.11.1",
54
+ "@medusajs/ui": "4.0.3"
55
+ },
56
+ "engines": {
57
+ "node": ">=20"
58
+ },
59
+ "dependencies": {
60
+ "@google-cloud/storage": "^7.7.0",
61
+ "lucide-react": "^0.548.0",
62
+ "multer": "^2.0.2",
63
+ "react-dropzone": "^14.3.8",
64
+ "ts-node": "^10.9.2",
65
+ "yalc": "^1.0.0-pre.53"
66
+ }
67
+ }