{"version":3,"file":"index.mjs","sources":["../../src/ee/index.ts"],"sourcesContent":["import { pick, isEqual } from 'lodash/fp';\nimport type { Logger } from '@strapi/logger';\nimport type { Core } from '@strapi/types';\n\nimport { readLicense, verifyLicense, fetchLicense, LicenseCheckError } from './license';\nimport { shiftCronExpression } from '../utils/cron';\n\nconst ONE_MINUTE = 1000 * 60;\n\ninterface EE {\n  enabled: boolean;\n  licenseInfo: {\n    licenseKey?: string;\n    features?: Array<{ name: string; [key: string]: any } | string>;\n    expireAt?: string;\n    seats?: number;\n    type?: string;\n  };\n  logger?: Logger;\n}\n\nconst ee: EE = {\n  enabled: false,\n  licenseInfo: {},\n};\n\nconst disable = (message: string) => {\n  // Prevent emitting ee.disable if it was already disabled\n  const shouldEmitEvent = ee.enabled !== false;\n\n  ee.logger?.warn(`${message} Switching to CE.`);\n  // Only keep the license key for potential re-enabling during a later check\n  ee.licenseInfo = pick('licenseKey', ee.licenseInfo);\n\n  ee.enabled = false;\n\n  if (shouldEmitEvent) {\n    // Notify EE features that they should be disabled\n    strapi.eventHub.emit('ee.disable');\n  }\n};\n\nconst enable = () => {\n  // Prevent emitting ee.enable if it was already enabled\n  const shouldEmitEvent = ee.enabled !== true;\n\n  ee.enabled = true;\n\n  if (shouldEmitEvent) {\n    // Notify EE features that they should be disabled\n    strapi.eventHub.emit('ee.enable');\n  }\n};\n\nlet initialized = false;\n\n/**\n * Optimistically enable EE if the format of the license is valid, only run once.\n */\nconst init = (licenseDir: string, logger?: Logger) => {\n  if (initialized) {\n    return;\n  }\n\n  initialized = true;\n  ee.logger = logger;\n\n  if (process.env.STRAPI_DISABLE_EE?.toLowerCase() === 'true') {\n    return;\n  }\n\n  try {\n    const license = process.env.STRAPI_LICENSE || readLicense(licenseDir);\n\n    if (license) {\n      ee.licenseInfo = verifyLicense(license);\n      enable();\n    }\n  } catch (error) {\n    if (error instanceof Error) {\n      disable(error.message);\n    } else {\n      disable('Invalid license.');\n    }\n  }\n};\n\n/**\n * Contact the license registry to update the license to its latest state.\n *\n * Store the result in database to avoid unecessary requests, and will fallback to that in case of a network failure.\n */\nconst onlineUpdate = async ({ strapi }: { strapi: Core.Strapi }) => {\n  const { get, commit, rollback } = (await strapi.db?.transaction()) as any;\n  const transaction = get();\n\n  try {\n    const storedInfo = await strapi.db\n      ?.queryBuilder('strapi::core-store')\n      .where({ key: 'ee_information' })\n      .select('value')\n      .first()\n      .transacting(transaction)\n      .forUpdate()\n      .execute()\n      .then((result: any) => (result ? JSON.parse(result.value) : result));\n\n    const shouldContactRegistry = (storedInfo?.lastCheckAt ?? 0) < Date.now() - ONE_MINUTE;\n    const result: {\n      license?: string | null;\n      error?: string;\n      lastCheckAt?: number;\n    } = { lastCheckAt: Date.now() };\n\n    const fallback = (error: Error) => {\n      if (error instanceof LicenseCheckError && error.shouldFallback && storedInfo?.license) {\n        ee.logger?.warn(\n          `${error.message} The last stored one will be used as a potential fallback.`\n        );\n        return storedInfo.license;\n      }\n\n      result.error = error.message;\n      disable(error.message);\n    };\n\n    if (!ee?.licenseInfo?.licenseKey) {\n      throw new Error('Missing license key.');\n    }\n\n    const license = shouldContactRegistry\n      ? await fetchLicense({ strapi }, ee.licenseInfo.licenseKey, strapi.config.get('uuid')).catch(\n          fallback\n        )\n      : storedInfo.license;\n\n    if (license) {\n      try {\n        // Verify license and check if its info changed\n        const newLicenseInfo = verifyLicense(license);\n        const licenseInfoChanged =\n          !isEqual(newLicenseInfo.features, ee.licenseInfo.features) ||\n          newLicenseInfo.seats !== ee.licenseInfo.seats ||\n          newLicenseInfo.type !== ee.licenseInfo.type;\n\n        // Store the new license info\n        ee.licenseInfo = newLicenseInfo;\n        const wasEnabled = ee.enabled;\n        validateInfo();\n\n        // Notify EE features\n        if (licenseInfoChanged && wasEnabled) {\n          strapi.eventHub.emit('ee.update');\n        }\n      } catch (error) {\n        if (error instanceof Error) {\n          disable(error.message);\n        } else {\n          disable('Invalid license.');\n        }\n      }\n    } else if (!shouldContactRegistry) {\n      disable(storedInfo.error);\n    }\n\n    if (shouldContactRegistry) {\n      result.license = license ?? null;\n      const query = strapi.db.queryBuilder('strapi::core-store').transacting(transaction);\n\n      if (!storedInfo) {\n        query.insert({ key: 'ee_information', value: JSON.stringify(result) });\n      } else {\n        query.update({ value: JSON.stringify(result) }).where({ key: 'ee_information' });\n      }\n\n      await query.execute();\n    }\n\n    await commit();\n  } catch (error) {\n    // Example of errors: SQLite does not support FOR UPDATE\n    await rollback();\n  }\n};\n\nconst validateInfo = () => {\n  if (typeof ee.licenseInfo.expireAt === 'undefined') {\n    throw new Error('Missing license key.');\n  }\n\n  const expirationTime = new Date(ee.licenseInfo.expireAt).getTime();\n\n  if (expirationTime < new Date().getTime()) {\n    return disable('License expired.');\n  }\n\n  enable();\n};\n\nconst checkLicense = async ({ strapi }: { strapi: Core.Strapi }) => {\n  const shouldStayOffline =\n    ee.licenseInfo.type === 'gold' &&\n    // This env variable support is temporarily used to ease the migration between online vs offline\n    process.env.STRAPI_DISABLE_LICENSE_PING?.toLowerCase() === 'true';\n\n  if (!shouldStayOffline) {\n    await onlineUpdate({ strapi });\n\n    strapi.cron.add({\n      onlineUpdate: {\n        task: () => onlineUpdate({ strapi }),\n        options: shiftCronExpression('0 0 */12 * * *'),\n      },\n    });\n  } else {\n    if (!ee.licenseInfo.expireAt) {\n      return disable('Your license does not have offline support.');\n    }\n\n    validateInfo();\n  }\n};\n\nconst list = () => {\n  return (\n    ee.licenseInfo.features?.map((feature) =>\n      typeof feature === 'object' ? feature : { name: feature }\n    ) || []\n  );\n};\n\nconst get = (featureName: string) => list().find((feature) => feature.name === featureName);\n\nexport default Object.freeze({\n  init,\n  checkLicense,\n\n  get isEE() {\n    return ee.enabled;\n  },\n\n  get seats() {\n    return ee.licenseInfo.seats;\n  },\n\n  features: Object.freeze({\n    list,\n    get,\n    isEnabled: (featureName: string) => get(featureName) !== undefined,\n  }),\n});\n"],"names":["strapi","get","result"],"mappings":"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"}