Secure your code as it's written. Use Snyk Code to scan source code in minutes - no build needed - and fix issues immediately.
if (cart.billing && typeof cart.billing[0].address === "object" &&
cart.billing[0].address._id === addressId) {
update.$unset["billing.0.address"] = "";
needToUpdate = true;
}
if (cart.shipping && typeof cart.shipping[0].address === "object" && cart.shipping[0].address._id === addressId) {
removeShippingAddresses(cart);
isShippingDeleting = true;
}
}
if (needToUpdate) {
try {
Cart.update({ _id: cartId }, update);
} catch (error) {
Logger.error(error);
throw new ReactionError("server-error", "Error updating cart");
}
}
if (needToUpdate || isShippingDeleting) {
if (isShippingDeleting) {
// if we remove shipping address from cart, we need to revert
// `cartWorkflow` to the `checkoutAddressBook` step.
Meteor.call("workflow/revertCartWorkflow", "checkoutAddressBook", cartId);
}
const updatedCart = Cart.findOne({ _id: cartId });
Promise.await(appEvents.emit("afterCartUpdate", {
cart: updatedCart,
updatedBy: Reaction.getUserId()
}));
if (!cart) {
throw new Error("afterCartUpdate hook run with no cart argument");
}
// refresh shipping quotes
try {
Meteor.call("shipping/updateShipmentQuotes", cartId);
} catch (error) {
Logger.error(`Error calling shipping/updateShipmentQuotes method in afterCartUpdate for cart with ID ${cartId}`, error);
}
// revert workflow to checkout shipping step.
try {
Meteor.call("workflow/revertCartWorkflow", "coreCheckoutShipping", cartId);
} catch (error) {
Logger.error("Error calling workflow/revertCartWorkflow method in afterCartUpdate", error);
}
// reset selected shipment method
if (cart.shipping && cart.shipping[0] && cart.shipping[0].shipmentMethod) {
Cart.update({ _id: cartId }, {
$unset: { "shipping.0.shipmentMethod": "" }
});
const updatedCart = Cart.findOne({ _id: cartId });
Promise.await(appEvents.emit("afterCartUpdate", cartId, updatedCart));
}
});
Hooks.Events.add("afterCoreInit", () => {
const currentMigrationVersion = Migrations._getControl().version;
const highestAvailableVersion = Migrations._list[Migrations._list.length - 1].version;
// Checks to ensure the app is running against a DB at the right migration state. Running the app
// with a wrong DB state will cause the app to malfunction
if (currentMigrationVersion > highestAvailableVersion) {
Logger.fatal(`You are running a Reaction install with migration version (${highestAvailableVersion}) below your current DB migration state (${currentMigrationVersion})`);
Logger.fatal(`Upgrade to a version of Reaction containing migration ${currentMigrationVersion} or higher.`);
Logger.fatal("If you really want to downgrade to this version, you should restore your DB to a previous state from your backup.");
process.exit(0);
} else if (!Meteor.isAppTest) {
try {
Migrations.migrateTo("latest");
} catch (error) {
Logger.error("Error while migrating", error);
// Make sure the migration control record is unlocked so they can attempt to run again next time
Migrations.unlock();
}
}
});
const { name } = payment;
const { canRefund, functions } = getPaymentMethodConfigByName(name);
if (!canRefund) throw new ReactionError("invalid", "Refunding not supported");
const context = Promise.await(getGraphQLContextInMeteorMethod(authUserId));
// Get total amount not yet refunded
const allRefundsBefore = Promise.await(functions.listRefunds(context, payment));
const refundTotalBefore = allRefundsBefore.reduce((sum, refund) => sum + refund.amount, 0);
const amountNotYetRefunded = payment.amount - refundTotalBefore;
const amountToRefund = Math.min(amountNotYetRefunded, amount);
const result = Promise.await(functions.createRefund(context, payment, amountToRefund));
if (!result.saved) {
Logger.fatal("Attempt to refund payment failed", order._id, paymentId, result.error);
throw new ReactionError("Attempt to refund payment failed", result.error);
}
// List refunds to see if we've now refunded the full amount of this payment
const allRefunds = Promise.await(functions.listRefunds(context, payment));
const refundTotal = allRefunds.reduce((sum, refund) => sum + refund.amount, 0);
// There could be JS math errors so we round to 3 decimal places when comparing
const isFullyRefunded = accounting.toFixed(refundTotal, 3) === accounting.toFixed(payment.amount, 3);
const updateResult = Orders.update(
{
"_id": orderId,
"payments._id": paymentId
},
{
).toArray();
if (products.length !== productIds.length) {
throw new ReactionError("not-found", "Some products not found");
}
if (!isInternalCall) {
const uniqueShopIds = _.uniq(products.map((product) => product.shopId));
for (const shopId of uniqueShopIds) {
await checkPermissions(["createProduct", "product/admin", "product/publish"], shopId); // eslint-disable-line no-await-in-loop
}
}
const success = await publishProductsToCatalog(productIds, context);
if (!success) {
Logger.error("Some Products could not be published to the Catalog.");
throw new ReactionError(
"server-error",
"Some Products could not be published to the Catalog. Make sure the parent product and its variants and options are visible."
);
}
return Catalog.find({ "product.productId": { $in: productIds } }).toArray();
}
// given that we `export` this function, there is an expectation that it can
// be imported and used elsewhere in the code. the use of `this` in this
// method requires that the context be Meteor, and further, `this.unblock()`
// assumes that this is being run as a Meteor method. Consider using a small
// function in the Meteor.method section below to call unblock, and pass any
// Meteor-defined data (e.g., userId) as a parameter to allow for this method
// to be reused.
this.unblock();
const shop = Shops.findOne(shopId);
const primaryShop = Reaction.getPrimaryShop();
if (!shop) {
const msg = `accounts/inviteShopMember - Shop ${shopId} not found`;
Logger.error(msg);
throw new Meteor.Error("not-found", msg);
}
if (!Reaction.hasPermission("reaction-accounts", this.userId, shopId)) {
Logger.error(`User ${this.userId} does not have reaction-accounts permissions`);
throw new ReactionError("access-denied", "Access denied");
}
const group = Groups.findOne({ _id: groupId }) || {};
// check to ensure that user has roles required to perform the invitation
if (!Reaction.canInviteToGroup({ group, user: Meteor.user() })) {
throw new ReactionError("access-denied", "Cannot invite to group");
}
if (group.slug === "owner") {
if (!user) {
Logger.error("sendResetPasswordEmail - User not found");
throw new ReactionError("not-found", "User not found");
}
let email = optionalEmail;
// pick the first email if we weren't passed an email.
if (!optionalEmail && user.emails && user.emails[0]) {
email = user.emails[0].address;
}
// make sure we have a valid email
if (!email || !user.emails || !user.emails.map((mailInfo) => mailInfo.address).includes(email)) {
Logger.error("sendResetPasswordEmail - Email not found");
throw new ReactionError("not-found", "Email not found");
}
// Create token for password reset
const token = Random.secret();
const when = new Date();
const tokenObj = { token, email, when };
Meteor.users.update(userId, {
$set: {
"services.password.reset": tokenObj
}
});
Meteor._ensure(user, "services", "password").reset = tokenObj;
async function sendResetEmail(context, account, email) {
const { collections } = context;
const { Shops, users } = collections;
// Make sure the user exists, and email is one of their addresses.
const user = await users.findOne({ _id: account.userId });
if (!user) {
Logger.error("sendResetAccountPasswordEmail - User not found");
throw new ReactionError("not-found", "User not found");
}
// make sure we have a valid email
if (!email || !user.emails || !user.emails.map((mailInfo) => mailInfo.address).includes(email)) {
Logger.error("sendResetPasswordEmail - Email not found");
throw new ReactionError("not-found", "Email not found");
}
// Create token for password reset
const tokenObj = generateVerificationTokenObject({ email });
const { value: updatedAccount } = await users.findOneAndUpdate({ _id: account.userId }, {
$set: {
"services.password.reset": tokenObj
}
"orders/shipmentShipped"(order, shipment) {
check(order, Object);
check(shipment, Object);
// TODO: Who should have access to ship shipments in a marketplace setting
// Should be anyone who has product in an order.
if (!Reaction.hasPermission("orders")) {
Logger.error("User does not have 'orders' permissions");
throw new Meteor.Error("access-denied", "Access Denied");
}
this.unblock();
let completedItemsResult;
let completedOrderResult;
const itemIds = shipment.items.map((item) => item._id);
// TODO: In the future, this could be handled by shipping delivery status
// REVIEW: This hook seems to run before the shipment has been marked as shipped
Hooks.Events.run("onOrderShipmentShipped", order, itemIds);
const workflowResult = Meteor.call("workflow/pushItemWorkflow", "coreOrderItemWorkflow/shipped", order, itemIds);
if (workflowResult === 1) {
export function buildAccountSearchRecord(accountId, updatedFields) {
Logger.debug("building account search record");
check(accountId, String);
check(updatedFields, Array);
const account = Accounts.findOne(accountId);
// let's ignore anonymous accounts
if (account && account.emails && account.emails.length) {
const accountSearch = {};
// Not all required fields are used in search
// We need to filter through fields that are used,
// and only update the search index if one of those fields were updated
// forceIndex is included to forceIndexing on startup, or when manually added
const searchableFields = ["forceIndex", "shopId", "emails", "firstName", "lastName", "phone"];
const shouldRunIndex = updatedFields && updatedFields.some((field) => searchableFields.includes(field));