From e5d530a78c68d2b9ee64011d8780467571db6969 Mon Sep 17 00:00:00 2001 From: Asterioxer <162615960+Asterioxer@users.noreply.github.com> Date: Fri, 16 May 2025 18:25:12 +0530 Subject: [PATCH 1/2] docs: improved README description for clarity Signed-off-by: Asterioxer <162615960+Asterioxer@users.noreply.github.com> --- README.md | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 299088c5bf..762429a509 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,10 @@ Mailchimp Open Commerce (formerly Reaction Commerce) +

+ A modern, API-first, and headless commerce platform built with Node.js, MongoDB, and GraphQL. It integrates smoothly with tools like npm, Docker, and Kubernetes. +

+

Open Commerce Website | Twitter | @@ -18,16 +22,16 @@ # Features - + - + - + @@ -187,6 +191,9 @@ Find a bug, a typo, or something that’s not documented well? We’d love for y We love your pull requests! Check out our [`Good First Issue`](https://github.com/reactioncommerce/reaction/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22) and [`Help Wanted`](https://github.com/reactioncommerce/reaction/issues?q=label%3A%22help+wanted%22) tags for good issues to tackle. Check out our [contributors guide](CONTRIBUTING.md) for more information +> ✅ This project welcomes contributions from developers of all skill levels! + + ### License Reaction is [GNU GPLv3 Licensed](./LICENSE.md) From ddb7e94786e20968311c6808d198db2d87223555 Mon Sep 17 00:00:00 2001 From: Asterioxer <162615960+Asterioxer@users.noreply.github.com> Date: Fri, 16 May 2025 19:23:43 +0530 Subject: [PATCH 2/2] Enhance Event Bus with validation, Redis Pub/Sub, and webhook support --- packages/api-core/src/util/apiKeyManager.js | 13 ++++++ packages/api-core/src/util/appEvents.js | 44 ++++++++++---------- packages/api-core/src/util/pubSub.js | 28 +++++++++++++ packages/api-core/src/util/webhookManager.js | 31 ++++++++++++++ 4 files changed, 95 insertions(+), 21 deletions(-) create mode 100644 packages/api-core/src/util/apiKeyManager.js create mode 100644 packages/api-core/src/util/pubSub.js create mode 100644 packages/api-core/src/util/webhookManager.js diff --git a/packages/api-core/src/util/apiKeyManager.js b/packages/api-core/src/util/apiKeyManager.js new file mode 100644 index 0000000000..feb29a880c --- /dev/null +++ b/packages/api-core/src/util/apiKeyManager.js @@ -0,0 +1,13 @@ +import crypto from "crypto"; + +const apiKeys = {}; + +export function createApiKey(systemName) { + const apiKey = crypto.randomBytes(20).toString("hex"); + apiKeys[apiKey] = { systemName, createdAt: new Date() }; + return apiKey; +} + +export function validateApiKey(key) { + return apiKeys.hasOwnProperty(key); +} diff --git a/packages/api-core/src/util/appEvents.js b/packages/api-core/src/util/appEvents.js index d1723d98d9..5ba995facf 100644 --- a/packages/api-core/src/util/appEvents.js +++ b/packages/api-core/src/util/appEvents.js @@ -1,31 +1,20 @@ import Logger from "@reactioncommerce/logger"; +import SimpleSchema from "simpl-schema"; +import { publishEvent } from "./pubSub"; // Import Redis Pub/Sub -/** - * This is a temporary events solution on our path to - * event streams and services. For now, some code relies - * on events happening synchronously and we need it to - * work in Fibers when running within Meteor. - */ +// Define schemas for validation +const eventSchemas = { + "orderCreated": new SimpleSchema({ orderId: String, userId: String, totalAmount: Number }), + "userRegistered": new SimpleSchema({ userId: String, email: String }) +}; -/** - * @summary calls each function in an array with args, one at a time - * @param {String} name Event name - * @param {Function[]} funcs List of functions to call - * @param {Array} args Arguments to pass to each function - * @returns {undefined} Promise that resolves with undefined after all - * functions in the list have been called - */ async function synchronousPromiseLoop(name, funcs, args) { const func = funcs.shift(); - - // One function failing should not prevent others from running, - // so catch and log try { await func(...args); } catch (error) { Logger.error(`Error in "${name}" consumer`, error); } - if (funcs.length) { await synchronousPromiseLoop(name, funcs, args); } @@ -48,8 +37,21 @@ class AppEvents { async emit(name, ...args) { if (this.stopped || !this.handlers[name]) return; - // Can't use forEach or map because we want each func to wait - // until the previous func promise resolves + // Validate event arguments if a schema exists + if (eventSchemas[name]) { + const validationContext = eventSchemas[name].newContext(); + validationContext.validate(args[0]); + + if (!validationContext.isValid()) { + Logger.error(`Validation failed for event "${name}":`, validationContext.validationErrors()); + return; + } + } + + // Publish event to Redis Pub/Sub + publishEvent(name, args[0]); + + // Execute local handlers await synchronousPromiseLoop(name, this.handlers[name].slice(0), args); } @@ -57,9 +59,9 @@ class AppEvents { if (!this.handlers[name]) { this.handlers[name] = []; } - this.handlers[name].push(func); } } export default new AppEvents(); +export { AppEvents }; \ No newline at end of file diff --git a/packages/api-core/src/util/pubSub.js b/packages/api-core/src/util/pubSub.js new file mode 100644 index 0000000000..fadfc4d668 --- /dev/null +++ b/packages/api-core/src/util/pubSub.js @@ -0,0 +1,28 @@ +import redis from "redis"; + +const redisUrl = process.env.REDIS_URL || "redis://localhost:6379"; + +export const redisClient = redis.createClient({ url: redisUrl }); + +redisClient.on("error", (err) => console.error("[RedisClient] Error:", err)); + +redisClient.connect(); + +/** + * Publish an event to Redis Pub/Sub + */ +export function publishEvent(eventType, payload) { + redisClient.publish(eventType, JSON.stringify(payload)); +} + +/** + * Subscribe to events in Redis + */ +export function subscribeEvent(eventType, handler) { + const subscriber = redisClient.duplicate(); + subscriber.connect().then(() => { + subscriber.subscribe(eventType, (message) => { + handler(JSON.parse(message)); + }); + }); +} diff --git a/packages/api-core/src/util/webhookManager.js b/packages/api-core/src/util/webhookManager.js new file mode 100644 index 0000000000..13ce5a3873 --- /dev/null +++ b/packages/api-core/src/util/webhookManager.js @@ -0,0 +1,31 @@ +import fetch from "node-fetch"; +import crypto from "crypto"; + +const registeredWebhooks = []; + +export function registerWebhook(eventType, url, apiKey) { + registeredWebhooks.push({ eventType, url, apiKey }); +} + +export function triggerWebhooks(eventType, payload) { + registeredWebhooks.filter(wh => wh.eventType === eventType).forEach(wh => { + sendWebhookRequest(wh, payload); + }); +} + +function sendWebhookRequest(webhook, payload) { + fetch(webhook.url, { + method: "POST", + headers: { + "Content-Type": "application/json", + "x-api-key": webhook.apiKey, + "x-signature": signPayload(payload), + }, + body: JSON.stringify(payload), + }).catch(err => console.error(`[WebhookManager] Error: ${err.message}`)); +} + +function signPayload(payload) { + const secret = process.env.WEBHOOK_SECRET || "defaultSecret"; + return crypto.createHmac("sha256", secret).update(JSON.stringify(payload)).digest("hex"); +}
FastReturns data in split seconds, and faster queries mean faster web pages
FastReturns data in milliseconds, and faster queries mean faster web pages
ProvenOpen Commerce fuels sites doing 10's of thousands of orders per day with 100's of thousands of products
ComposableA flexible plugin system allows you to pick and choose which integrations work best for you
Multi-tenantHost multiple shops in the same installation
ScalableStart out with a single server and scale up to hundreds
Flexible ProductsAllows Products, with options and variants to fit a wide variety of needs
Flexible ProductsSupports products with customizable options and variants to meet a wide range of business needs.
InventoryTrack inventory, allow or disallow backorders and more
ShippingIntegrate with a shipping rate provider or build your own custom table
TaxesIntegrate with a tax rate provider or build your own custom tax table
FulfillmentFlexible fulfillment system allows you create your own fulfillment methods
FulfillmentFlexible fulfillment system allows you create self-customized fulfillment methods
Order TrackingView and manage your orders in the included admin system
EmailsCustomizable templates for Order confirmations and more
OpenFully open source. Never be locked in again