import {forbidden, ok, serverError} from 'wix-http-functions'; import {authentication} from 'wix-members-backend'; import wixData from 'wix-data'; import {contacts} from 'wix-crm-backend'; import wixSecretsBackend from 'wix-secrets-backend'; // Validates that a request is valid, ie that the requesting server has used // a valid API key, ie one that matches the one held in the Wix secrets manager. // Parameters: // request the request // data the data in the request which must include // key the API key // secret_name the name of the secret in the Wix secrets manager that holds the API key function validateRequest(request, data) { return wixSecretsBackend.getSecret(data.secret_name) .then((secret) => { if (secret === data.key) { return true; } else { console.log("MRBS: invalid API key passed by IP address " + request.headers['x-real-ip']); return false; } }) .catch((error) => { console.error(error); return false; }) } // The exported functions work by firing off two promises in parallel: the first // validates that the request comes from an authorised server and the second does // the actual work. When the two promises have been resolved or rejected, this // function processes the promise results and issues the appropriate response. function processPromiseResults(promiseResults) { let result = { "headers": { "Content-Type": "application/json" } } if ((promiseResults[0].status === 'rejected') || (promiseResults[1].status === 'rejected')) { result.body = "internal server error"; return serverError(result); } else if (promiseResults[0].value === false) { result.body = "forbidden"; return forbidden(result); } else { result.body = promiseResults[1].value; return ok(result); } } // Validates a member's email login and password. Returns a boolean. // Request data parameters: // email the member's login email address // password the password export async function post_validateMember(request) { const data = await request.body.json(); const validateRequestPromise = validateRequest(request, data); const validateMemberPromise = authentication.login(data.email, data.password) .then(() => { return true; }) .catch((error) => { // If the email address and password are not valid then we will get // an UNAUTHORIZED error. If it's any other kind then log it. if (error.details.applicationError.code === "UNAUTHORIZED") { console.error(error); } // Return false whatever the error return false; }); return Promise.allSettled([validateRequestPromise, validateMemberPromise]) .then((promiseResults) => { return processPromiseResults(promiseResults); }) } // Gets a member's details given an email address. Returns a JSON object or NULL. // Request data parameters: // email the member's login email address export async function post_getMemberByEmail(request) { const data = await request.body.json(); const options = { "suppressAuth": true, "suppressHooks": true }; const validateRequestPromise = validateRequest(request, data); const getMemberPromise = wixData.query("Members/PrivateMembersData") .eq("loginEmail", data.email) .limit(1) .find(options) .then((members) => { if(members.items.length > 0) { let member = members.items[0]; // Now we've got the member we have to get (a) their full details (including // custom fields, which aren't in PrivateMembersData) from Contacts using // the id and (b) their badges from Members/Badges. Get these two sets of // data in parallel using promises. const getContactPromise = contacts.getContact(member._id, {suppressAuth: true}) .then((contact) => { return { member: member, contact: contact }; }) .catch((error) => { console.error(error); return null; }); const getBadgesPromise = wixData.query("Members/Badges") .find() .then((results) => { return results.items; } ); return Promise.allSettled([getContactPromise, getBadgesPromise]) .then((promiseResults) => { if ((promiseResults[0].status === 'fulfilled') && (promiseResults[1].status ==='fulfilled')) { let result = promiseResults[0].value; result.badges = []; // Iterate through the badges checking if this member has the badge if (promiseResults[1].value) { promiseResults[1].value.forEach(badge => { if (badge.members.includes(member._id)) { result.badges.push(badge.title); } }); } return result; } else { return null; } }) } else { return null; } }) .catch((error) => { console.error(error); return null; }); return Promise.allSettled([validateRequestPromise, getMemberPromise]) .then((promiseResults) => { return processPromiseResults(promiseResults); }) } // Returns an array of members indexed by 'username' and 'display_name' // Request data parameters: // limit (optional) the limit to be used in each query. Defaults to 50. // display_name_property (optional) the member property to be used for the display name. // Typically either 'name' (the default) or 'nickname'. export async function post_getMemberNames(request) { const data = await request.body.json(); const displayNameProperty = data.display_name_property ?? 'name'; const options = { "suppressAuth": true, "suppressHooks": true }; const defaultLimit = 50; let memberNames = []; let limit = defaultLimit; if (data.limit !== undefined) { limit = parseInt(data.limit, 10); if (isNaN(limit) || (limit <= 0)) { limit =defaultLimit; } } function extractMemberNames(items) { let result = []; items.forEach(function(item) { result.push({ username: item.loginEmail, display_name: ((item[displayNameProperty] === undefined) || (item[displayNameProperty] === null) || (item[displayNameProperty] === '')) ? item.loginEmail : item[displayNameProperty] }); }); return result; } const validateRequestPromise = validateRequest(request, data); const getMemberNamesPromise = wixData.query("Members/PrivateMembersData") .limit(limit) .find(options) .then(async (results) => { memberNames = memberNames.concat(extractMemberNames(results.items)); while (results.hasNext()) { results = await results.next(); memberNames = memberNames.concat(extractMemberNames(results.items)); } }) .catch((error) => { console.error(error); }) .then(() => { return memberNames; }) return Promise.allSettled([validateRequestPromise, getMemberNamesPromise]) .then((promiseResults) => { return processPromiseResults(promiseResults); }) }