@agnostack/next-shopify
Version:
Please contact agnoStack via info@agnostack.com for any questions
124 lines • 7.61 kB
JavaScript
;
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __rest = (this && this.__rest) || function (s, e) {
var t = {};
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
t[p] = s[p];
if (s != null && typeof Object.getOwnPropertySymbols === "function")
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
t[p[i]] = s[p[i]];
}
return t;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.getAuthErrorResponse = exports.getCallback = exports.getPrepareWebhookHandlers = exports.getInitializedShopify = exports.allowLocalBypass = exports.getSecurityHeader = void 0;
const chalk_1 = __importDefault(require("chalk"));
require("@shopify/shopify-api/adapters/node");
const shopify_api_1 = require("@shopify/shopify-api");
const shared_1 = require("../../shared");
const VALID_WEBHOOK_TOPICS = Object.keys(shared_1.API_WEBHOOK_TOPICS);
// NOTE rejection response shows accepted value is "frame-ancestors https://admin.shopify.com https://[shop].myshopify.com"
const getSecurityHeader = (shop, { isEmbeddedApp } = {}) => (((0, shared_1.stringNotEmpty)(shop) && (0, shared_1.isTrue)(isEmbeddedApp))
? `frame-ancestors https://${encodeURIComponent(shop)} https://admin.shopify.com;`
: "frame-ancestors 'none';");
exports.getSecurityHeader = getSecurityHeader;
// NOTE: this should handle running in next to test non-shopify functionality
const allowLocalBypass = ({ SHOPIFY_RUN_MODE, APP_CONFIG } = {}) => ((SHOPIFY_RUN_MODE !== 'cli') &&
(!APP_CONFIG.apiSecretKey ||
!APP_CONFIG.hostName ||
!APP_CONFIG.apiKey ||
!APP_CONFIG.scopes));
exports.allowLocalBypass = allowLocalBypass;
const getInitializedShopify = (serverRuntimeConfig) => {
// NOTE: this should handle running in next to test non-shopify functionality
const shouldBypassProtect = (0, exports.allowLocalBypass)(serverRuntimeConfig);
if (shouldBypassProtect) {
console.info(`${chalk_1.default.yellowBright('next-shopify')} - [${chalk_1.default.bold('getInitializedShopify')}]: ${`Bypassing local protect - should ONLY happen when running in next to test non-shopify functionality!`}`);
return undefined;
}
return (0, shopify_api_1.shopifyApi)(serverRuntimeConfig.APP_CONFIG);
};
exports.getInitializedShopify = getInitializedShopify;
const getPrepareWebhookHandlers = (serverRuntimeConfig) => {
const { APP_WEBHOOKS, APP_WEBHOOKS_MUTABLE_TOPICS } = serverRuntimeConfig;
return (params) => (Object.entries((0, shared_1.ensureObject)(APP_WEBHOOKS)).reduce((_webhookHandlers, [webhookTopic, webhooks]) => {
var _a;
if (!VALID_WEBHOOK_TOPICS.includes(webhookTopic)) {
console.warn(`Ignoring unsupported webhook topic: ${webhookTopic}`);
return _webhookHandlers;
}
if ((((_a = APP_WEBHOOKS_MUTABLE_TOPICS === null || APP_WEBHOOKS_MUTABLE_TOPICS === void 0 ? void 0 : APP_WEBHOOKS_MUTABLE_TOPICS[webhookTopic]) === null || _a === void 0 ? void 0 : _a.enabled) != undefined) && !(0, shared_1.isTrue)(APP_WEBHOOKS_MUTABLE_TOPICS[webhookTopic].enabled)) {
console.warn(`Ignoring disabled webhook topic: ${webhookTopic}`);
return _webhookHandlers;
}
const queryString = new URLSearchParams(params).toString();
return Object.assign(Object.assign({}, _webhookHandlers), { [webhookTopic]: (0, shared_1.ensureArray)(webhooks).reduce((_topicWebhooks, _a) => {
var { url, path, callback: _callback, callbackUrl: _callbackUrl, deliveryMethod = shopify_api_1.DeliveryMethod.Http } = _a,
// NOTE: this lets us support more advanced webhook types (EventBridgeWebhookHandler or PubSubWebhookHandler)
webhookConfig = __rest(_a, ["url", "path", "callback", "callbackUrl", "deliveryMethod"]);
const callbackUrl = (0, shared_1.appendQuery)(_callbackUrl || url || path, queryString);
console.info(`${chalk_1.default.yellowBright('next-shopify')} - [${chalk_1.default.bold(webhookTopic)}]: ${`Adding webhook handler [${deliveryMethod}]${callbackUrl ? ` using callbackUrl: ${callbackUrl}` : ''}.`}`);
return [
..._topicWebhooks,
Object.assign(Object.assign(Object.assign({ deliveryMethod }, webhookConfig), callbackUrl && { callbackUrl }), (callbackUrl || _callback) && {
callback: (topic, shop, rawBody, webhookId, ...params) => __awaiter(void 0, void 0, void 0, function* () {
if (_callback) {
console.log(`Webhook callback for ${shop}:${topic}:${webhookId}`);
return _callback(topic, shop, rawBody, webhookId, ...params);
}
return true;
}),
})
];
}, []) });
}, {}));
};
exports.getPrepareWebhookHandlers = getPrepareWebhookHandlers;
const getCallback = (data, { reAuth, isOnline }) => {
const authCallbacks = reAuth ? {
online: data[shared_1.API_ROUTE_NAMES.AUTH_ONLINE_CALLBACK_REAUTH],
offline: data[shared_1.API_ROUTE_NAMES.AUTH_OFFLINE_CALLBACK_REAUTH],
} : {
online: data[shared_1.API_ROUTE_NAMES.AUTH_ONLINE_CALLBACK],
offline: data[shared_1.API_ROUTE_NAMES.AUTH_OFFLINE_CALLBACK],
};
return isOnline
? authCallbacks.online
: authCallbacks.offline || authCallbacks.online;
};
exports.getCallback = getCallback;
const getAuthErrorResponse = (serverRuntimeConfig, { error }) => {
const { message, data } = error;
const { PAGE_PATHS } = serverRuntimeConfig;
// TODO determine appropriate url in case of an error
const reauthorizationUrl = (data === null || data === void 0 ? void 0 : data.redirectUrl) || `${PAGE_PATHS[shared_1.PAGE_ROUTE_NAMES.ERROR]}`;
// NOTE: old example code below
// const { API_URLS } = serverRuntimeConfig
// const queryParams = new URLSearchParams({
// shop,
// reAuth: (error instanceof ShopifySessionAuthenticationError),
// }).toString()
// const reauthorizationUrl = data?.redirectUrl || `${API_URLS[API_ROUTE_NAMES.AUTH_ONLINE]}?${queryParams}` // HMMMMM: do we not need host here??
return {
statusCode: 403,
message,
headers: {
'X-Shopify-API-Request-Failure-Reauthorize': '1', // TODO: what about cyclical loop on bad auth??
'X-Shopify-API-Request-Failure-Reauthorize-Url': reauthorizationUrl,
},
};
};
exports.getAuthErrorResponse = getAuthErrorResponse;
//# sourceMappingURL=shopify.js.map