{"version":3,"file":"ensure-installed-on-shop.mjs","sources":["../../../../src/middlewares/ensure-installed-on-shop.ts"],"sourcesContent":["import {Request, Response, NextFunction} from 'express';\nimport {Session, Shopify} from '@shopify/shopify-api';\n\nimport {redirectToAuth} from '../redirect-to-auth';\nimport {AppConfigInterface} from '../config-types';\nimport {ApiAndConfigParams} from '../types';\nimport {AppInstallations} from '../app-installations';\n\nimport {EnsureInstalledMiddleware} from './types';\nimport {addCSPHeader} from './csp-headers';\nimport {validateAuthenticatedSession} from './validate-authenticated-session';\nimport {hasValidAccessToken} from './has-valid-access-token';\n\ntype EnsureInstalledParams = ApiAndConfigParams;\n\nexport function ensureInstalled({\n  api,\n  config,\n}: EnsureInstalledParams): EnsureInstalledMiddleware {\n  return function ensureInstalledOnShop() {\n    return async (req: Request, res: Response, next: NextFunction) => {\n      config.logger.debug('Running ensureInstalledOnShop');\n\n      if (!api.config.isEmbeddedApp) {\n        config.logger.warning(\n          'ensureInstalledOnShop() should only be used in embedded apps; calling validateAuthenticatedSession() instead',\n        );\n\n        return validateAuthenticatedSession({api, config})()(req, res, next);\n      }\n\n      const shop = getRequestShop(api, config, req, res);\n      if (!shop) {\n        return undefined;\n      }\n\n      config.logger.debug('Checking if shop has installed the app', {shop});\n\n      const sessionId = api.session.getOfflineId(shop);\n      const session = await config.sessionStorage.loadSession(sessionId);\n\n      const exitIframeRE = new RegExp(`^${config.exitIframePath}`, 'i');\n      if (!session && !req.originalUrl.match(exitIframeRE)) {\n        config.logger.debug(\n          'App installation was not found for shop, redirecting to auth',\n          {shop},\n        );\n\n        return redirectToAuth({req, res, api, config});\n      }\n\n      if (api.config.isEmbeddedApp && req.query.embedded !== '1') {\n        if (await sessionHasValidAccessToken(api, config, session)) {\n          await embedAppIntoShopify(api, config, req, res, shop);\n          return undefined;\n        } else {\n          config.logger.info(\n            'Found a session, but it is not valid. Redirecting to auth',\n            {shop},\n          );\n\n          return redirectToAuth({req, res, api, config});\n        }\n      }\n\n      addCSPHeader(api, req, res);\n\n      config.logger.debug('App is installed and ready to load', {shop});\n\n      return next();\n    };\n  };\n}\n\nexport function deleteAppInstallationHandler(\n  appInstallations: AppInstallations,\n  config: AppConfigInterface,\n) {\n  return async function (\n    _topic: string,\n    shop: string,\n    _body: any,\n    _webhookId: string,\n  ) {\n    config.logger.debug('Deleting shop sessions', {shop});\n\n    await appInstallations.delete(shop);\n  };\n}\n\nfunction getRequestShop(\n  api: Shopify,\n  config: AppConfigInterface,\n  req: Request,\n  res: Response,\n): string | undefined {\n  if (typeof req.query.shop !== 'string') {\n    config.logger.error(\n      'ensureInstalledOnShop did not receive a shop query argument',\n      {shop: req.query.shop},\n    );\n\n    res.status(400);\n    res.send('No shop provided');\n    return undefined;\n  }\n\n  const shop = api.utils.sanitizeShop(req.query.shop);\n\n  if (!shop) {\n    config.logger.error(\n      'ensureInstalledOnShop did not receive a valid shop query argument',\n      {shop: req.query.shop},\n    );\n\n    res.status(422);\n    res.send('Invalid shop provided');\n    return undefined;\n  }\n\n  return shop;\n}\n\nasync function sessionHasValidAccessToken(\n  api: Shopify,\n  config: AppConfigInterface,\n  session: Session | undefined,\n): Promise<boolean> {\n  if (!session) {\n    return false;\n  }\n\n  try {\n    return (\n      session.isActive(api.config.scopes) &&\n      (await hasValidAccessToken(api, session))\n    );\n  } catch (error) {\n    config.logger.error(`Could not check if session was valid: ${error}`, {\n      shop: session.shop,\n    });\n    return false;\n  }\n}\n\nasync function embedAppIntoShopify(\n  api: Shopify,\n  config: AppConfigInterface,\n  req: Request,\n  res: Response,\n  shop: string,\n): Promise<void> {\n  let embeddedUrl: string;\n  try {\n    embeddedUrl = await api.auth.getEmbeddedAppUrl({\n      rawRequest: req,\n      rawResponse: res,\n    });\n  } catch (error) {\n    config.logger.error(\n      `ensureInstalledOnShop did not receive a host query argument`,\n      {shop},\n    );\n\n    res.status(400);\n    res.send('No host provided');\n    return;\n  }\n\n  config.logger.debug(\n    `Request is not embedded but app is. Redirecting to ${embeddedUrl} to embed the app`,\n    {shop},\n  );\n\n  res.redirect(embeddedUrl + req.path);\n}\n"],"names":[],"mappings":"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"}