diff --git a/packages/backend/package.json b/packages/backend/package.json index 768cf2e1..b6cca056 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -50,6 +50,7 @@ "knex": "^2.4.0", "lodash.get": "^4.4.2", "luxon": "2.5.2", + "memory-cache": "^0.2.0", "morgan": "^1.10.0", "multer": "1.4.5-lts.1", "nodemailer": "6.7.0", @@ -103,6 +104,7 @@ "@types/http-errors": "^1.8.1", "@types/jsonwebtoken": "^8.5.8", "@types/lodash.get": "^4.4.6", + "@types/memory-cache": "^0.2.2", "@types/morgan": "^1.9.3", "@types/multer": "1.4.7", "@types/node": "^16.10.2", diff --git a/packages/backend/src/config/app.ts b/packages/backend/src/config/app.ts index 4f30aab6..29893e18 100644 --- a/packages/backend/src/config/app.ts +++ b/packages/backend/src/config/app.ts @@ -32,6 +32,7 @@ type AppConfig = { bullMQDashboardPassword: string; telemetryEnabled: boolean; requestBodySizeLimit: string; + licenseKey: string; }; const host = process.env.HOST || 'localhost'; @@ -40,7 +41,7 @@ const port = process.env.PORT || '3000'; const serveWebAppSeparately = process.env.SERVE_WEB_APP_SEPARATELY === 'true' ? true : false; -let apiUrl = (new URL(`${protocol}://${host}:${port}`)).toString(); +let apiUrl = new URL(`${protocol}://${host}:${port}`).toString(); apiUrl = apiUrl.substring(0, apiUrl.length - 1); // use apiUrl by default, which has less priority over the following cases @@ -48,14 +49,14 @@ let webAppUrl = apiUrl; if (process.env.WEB_APP_URL) { // use env. var. if provided - webAppUrl = (new URL(process.env.WEB_APP_URL)).toString(); + webAppUrl = new URL(process.env.WEB_APP_URL).toString(); webAppUrl = webAppUrl.substring(0, webAppUrl.length - 1); } else if (serveWebAppSeparately) { // no env. var. and serving separately, sign of development - webAppUrl = 'http://localhost:3001' + webAppUrl = 'http://localhost:3001'; } -let webhookUrl = (new URL(process.env.WEBHOOK_URL || apiUrl)).toString(); +let webhookUrl = new URL(process.env.WEBHOOK_URL || apiUrl).toString(); webhookUrl = webhookUrl.substring(0, webhookUrl.length - 1); const appEnv = process.env.APP_ENV || 'development'; @@ -91,6 +92,7 @@ const appConfig: AppConfig = { webhookUrl, telemetryEnabled: process.env.TELEMETRY_ENABLED === 'false' ? false : true, requestBodySizeLimit: '1mb', + licenseKey: process.env.LICENSE_KEY, }; if (!appConfig.encryptionKey) { diff --git a/packages/backend/src/graphql/queries/get-license.ee.ts b/packages/backend/src/graphql/queries/get-license.ee.ts new file mode 100644 index 00000000..58578e71 --- /dev/null +++ b/packages/backend/src/graphql/queries/get-license.ee.ts @@ -0,0 +1,11 @@ +import checkLicense from '../../helpers/checkLicense.ee'; + +const getLicense = async () => { + const license = await checkLicense(); + + return { + type: license ? 'ee' : 'ce', + }; +}; + +export default getLicense; diff --git a/packages/backend/src/graphql/query-resolvers.ts b/packages/backend/src/graphql/query-resolvers.ts index 9e4f6489..35b6280a 100644 --- a/packages/backend/src/graphql/query-resolvers.ts +++ b/packages/backend/src/graphql/query-resolvers.ts @@ -10,6 +10,7 @@ import getExecutions from './queries/get-executions'; import getExecutionSteps from './queries/get-execution-steps'; import getDynamicData from './queries/get-dynamic-data'; import getCurrentUser from './queries/get-current-user'; +import getLicense from './queries/get-license.ee'; import healthcheck from './queries/healthcheck'; const queryResolvers = { @@ -25,6 +26,7 @@ const queryResolvers = { getExecutionSteps, getDynamicData, getCurrentUser, + getLicense, healthcheck, }; diff --git a/packages/backend/src/graphql/schema.graphql b/packages/backend/src/graphql/schema.graphql index a33d6f06..fecf5ceb 100644 --- a/packages/backend/src/graphql/schema.graphql +++ b/packages/backend/src/graphql/schema.graphql @@ -29,6 +29,7 @@ type Query { parameters: JSONObject ): JSONObject getCurrentUser: User + getLicense: GetLicense healthcheck: AppHealth } @@ -453,6 +454,10 @@ type AppHealth { version: String } +type GetLicense { + type: String +} + schema { query: Query mutation: Mutation diff --git a/packages/backend/src/helpers/checkLicense.ee.ts b/packages/backend/src/helpers/checkLicense.ee.ts new file mode 100644 index 00000000..1ea04e59 --- /dev/null +++ b/packages/backend/src/helpers/checkLicense.ee.ts @@ -0,0 +1,31 @@ +import axios from 'axios'; +import appConfig from '../config/app'; +import memoryCache from 'memory-cache'; + +const CACHE_DURATION = 1000 * 60 * 60 * 24; // 24 hours in milliseconds + +const checkLicense = async () => { + const licenseKey = appConfig.licenseKey; + + if (!licenseKey) { + return false; + } + + const url = 'https://license.automatisch.io/api/v1/licenses/verify'; + const cachedResponse = memoryCache.get(url); + + if (cachedResponse) { + return cachedResponse; + } else { + try { + const { data } = await axios.post(url, { licenseKey }); + memoryCache.put(url, data.verified, CACHE_DURATION); + + return data.verified; + } catch (error) { + return false; + } + } +}; + +export default checkLicense; diff --git a/yarn.lock b/yarn.lock index 10a2c171..f2276b23 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3956,6 +3956,11 @@ resolved "https://registry.yarnpkg.com/@types/luxon/-/luxon-2.3.1.tgz#e34763178b46232e4c5f079f1706e18692415519" integrity sha512-nAPUltOT28fal2eDZz8yyzNhBjHw1NEymFBP7Q9iCShqpflWPybxHbD7pw/46jQmT+HXOy1QN5hNTms8MOTlOQ== +"@types/memory-cache@^0.2.2": + version "0.2.2" + resolved "https://registry.yarnpkg.com/@types/memory-cache/-/memory-cache-0.2.2.tgz#f8fb6d8aa0eb006ed44fc659bf8bfdc1a5cc57fa" + integrity sha512-xNnm6EkmYYhTnLiOHC2bdKgcYY5qjjrq5vl9KXD2nh0em0koZoFS500EL4Q4V/eW+A3P7NC7P7GIYzNOSQp7jQ== + "@types/mime@^1": version "1.3.2" resolved "https://registry.yarnpkg.com/@types/mime/-/mime-1.3.2.tgz#93e25bf9ee75fe0fd80b594bc4feb0e862111b5a" @@ -11874,6 +11879,11 @@ memfs@^3.1.2, memfs@^3.2.2: dependencies: fs-monkey "1.0.3" +memory-cache@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/memory-cache/-/memory-cache-0.2.0.tgz#7890b01d52c00c8ebc9d533e1f8eb17e3034871a" + integrity sha512-OcjA+jzjOYzKmKS6IQVALHLVz+rNTMPoJvCztFaZxwG14wtAW7VRZjwTQu06vKCYOxh4jVnik7ya0SXTB0W+xA== + meow@^8.0.0: version "8.1.2" resolved "https://registry.yarnpkg.com/meow/-/meow-8.1.2.tgz#bcbe45bda0ee1729d350c03cffc8395a36c4e897"