diff --git a/docs/v3.x/concepts/configurations.md b/docs/v3.x/concepts/configurations.md index 57cea84568..6e3c7f2859 100644 --- a/docs/v3.x/concepts/configurations.md +++ b/docs/v3.x/concepts/configurations.md @@ -50,6 +50,26 @@ module.exports = ({ env }) => { ## Environment variables +### List of Strapi's environment variables + +Some settings can only be modified through environment variables. Here is a list of those settings are associated environment variable names: + +| name | description | type | default | +| ------------------------------------ | --------------------------------------------------------------------------------------------------------------------- | ------- | --------------- | +| `STRAPI_DISABLE_UPDATE_NOTIFICATION` | Don't show the notification message about updating strapi in the terminal | boolean | `false` | +| `STRAPI_HIDE_STARTUP_MESSAGE` | Don't show the startup message in the terminal | boolean | `false` | +| `STRAPI_TELEMETRY_DISABLED` | Don't send telemetry usage data to Strapi | boolean | `false` | +| `STRAPI_LOG_TIMESTAMP` | Add the timestamp info in logs | boolean | `false` | +| `STRAPI_LOG_LEVEL` | Select the level of logs among `fatal`, `error`, `warn`, `info`, `debug`, `trace` | string | `'info'` | +| `STRAPI_LOG_FORCE_COLOR` | Force colors to be displayed even in environments that are not supposed to have colors enabled (ex: outside of a TTY) | boolean | `true` | +| `STRAPI_LOG_PRETTY_PRINT` | Log lines are displayed as text instead of as object | boolean | `true` | +| `STRAPI_LICENSE` | The license key to activate the Enterprise Edition | string | `undefined` | +| `NODE_ENV` | Type of environment where the app is running | string | `'development'` | +| `BROWSER` | Open the admin panel in the browser after startup | boolean | `true` | +| `ENV_PATH` | Path to the file that contains your environment variables | string | `'./.env'` | + +### Configuration using environment variables + In most use cases you will have different configurations between your environments. For example: your database credentials. Instead of writing those credentials into your configuration files, you can define those variables in a `.env` file at the root of your application. @@ -86,7 +106,7 @@ module.exports = ({ env }) => ({ }); ``` -### Casting environment variables +#### Casting environment variables ```js // Returns the env if defined without casting it diff --git a/examples/getstarted/.gitignore b/examples/getstarted/.gitignore index a96be48f07..cec49b62d7 100644 --- a/examples/getstarted/.gitignore +++ b/examples/getstarted/.gitignore @@ -98,7 +98,7 @@ node_modules ############################ -# Tests +# Strapi ############################ testApp @@ -110,3 +110,5 @@ exports documentation exports + +.strapi-updater.json diff --git a/packages/strapi/lib/core/app-configuration/__tests__/env-helper.test.js b/packages/strapi-utils/lib/__tests__/env-helper.test.js similarity index 100% rename from packages/strapi/lib/core/app-configuration/__tests__/env-helper.test.js rename to packages/strapi-utils/lib/__tests__/env-helper.test.js diff --git a/packages/strapi/lib/core/app-configuration/env-helper.js b/packages/strapi-utils/lib/env-helper.js similarity index 100% rename from packages/strapi/lib/core/app-configuration/env-helper.js rename to packages/strapi-utils/lib/env-helper.js diff --git a/packages/strapi-utils/lib/index.js b/packages/strapi-utils/lib/index.js index 454a1ed8f1..a95e5a2954 100644 --- a/packages/strapi-utils/lib/index.js +++ b/packages/strapi-utils/lib/index.js @@ -31,6 +31,7 @@ const { getConfigUrls, getAbsoluteAdminUrl, getAbsoluteServerUrl } = require('./ const { generateTimestampCode } = require('./code-generator'); const contentTypes = require('./content-types'); const webhook = require('./webhook'); +const env = require('./env-helper'); module.exports = { yup, @@ -61,4 +62,5 @@ module.exports = { stringEquals, contentTypes, webhook, + env, }; diff --git a/packages/strapi/lib/Strapi.js b/packages/strapi/lib/Strapi.js index 9fed1a8280..bca04a9a2e 100644 --- a/packages/strapi/lib/Strapi.js +++ b/packages/strapi/lib/Strapi.js @@ -27,6 +27,7 @@ const { createCoreStore, coreStoreModel } = require('./services/core-store'); const createEntityService = require('./services/entity-service'); const entityValidator = require('./services/entity-validator'); const createTelemetry = require('./services/metrics'); +const createUpdateNotifier = require('./utils/update-notifier'); const ee = require('./utils/ee'); /** @@ -66,6 +67,8 @@ class Strapi { this.eventHub = createEventHub(); this.requireProjectBootstrap(); + + createUpdateNotifier(this).notify(); } get EE() { diff --git a/packages/strapi/lib/core/app-configuration/config-loader.js b/packages/strapi/lib/core/app-configuration/config-loader.js index fda1220f8e..65b1eea7c9 100644 --- a/packages/strapi/lib/core/app-configuration/config-loader.js +++ b/packages/strapi/lib/core/app-configuration/config-loader.js @@ -2,9 +2,7 @@ const path = require('path'); const fs = require('fs'); -const { templateConfiguration } = require('strapi-utils'); - -const env = require('./env-helper'); +const { templateConfiguration, env } = require('strapi-utils'); module.exports = dir => { if (!fs.existsSync(dir)) return {}; diff --git a/packages/strapi/lib/utils/update-notifier/index.js b/packages/strapi/lib/utils/update-notifier/index.js new file mode 100644 index 0000000000..0a15ae83d3 --- /dev/null +++ b/packages/strapi/lib/utils/update-notifier/index.js @@ -0,0 +1,86 @@ +'use strict'; +const packageJson = require('package-json'); +const Configstore = require('configstore'); +const semver = require('semver'); +const boxen = require('boxen'); +const chalk = require('chalk'); +const path = require('path'); +const pkg = require('../../../package'); +const { env } = require('strapi-utils'); +const CHECK_INTERVAL = 1000 * 60 * 60 * 24 * 1; // 1 day +const NOTIF_INTERVAL = 1000 * 60 * 60 * 24 * 7; // 1 week +const boxenOptions = { + padding: 1, + margin: 1, + align: 'center', + borderColor: 'yellow', + borderStyle: 'round', +}; + +const geUpdatetMessage = (newVersion, currentVersion) => { + const currentVersionLog = chalk.dim(currentVersion); + const newVersionLog = chalk.green(newVersion); + const releaseLink = chalk.bold('https://github.com/strapi/strapi/releases'); + + return ` +A new version of Strapi is available ${currentVersionLog} → ${newVersionLog} +Check out the new the releases at: ${releaseLink} +`.trim(); +}; + +const createUpdateNotifier = strapi => { + const config = new Configstore( + pkg.name, + {}, + { configPath: path.join(strapi.dir, '.strapi-updater.json') } + ); + + const checkUpdate = async checkInterval => { + const now = Date.now(); + const lastUpdateCheck = config.get('lastUpdateCheck') || 0; + if (lastUpdateCheck + checkInterval > now) { + return; + } + + try { + const res = await packageJson(pkg.name); + if (res.version) { + config.set('latest', res.version); + config.set('lastUpdateCheck', now); + } + } catch { + // silence error if offline + } + }; + + const display = notifInterval => { + const now = Date.now(); + const latestVersion = config.get('latest'); + const lastNotification = config.get('lastNotification') || 0; + if ( + !process.stdout.isTTY || + lastNotification + notifInterval > now || + !semver.valid(latestVersion) || + !semver.valid(pkg.version) || + semver.lte(latestVersion, pkg.version) + ) { + return; + } + + const message = boxen(geUpdatetMessage(latestVersion, pkg.version), boxenOptions); + config.set('lastNotification', now); + console.log(message); + }; + + return { + notify({ checkInterval = CHECK_INTERVAL, notifInterval = NOTIF_INTERVAL } = {}) { + if (env.bool('STRAPI_DISABLE_UPDATE_NOTIFICATION', false)) { + return; + } + display(notifInterval); + checkUpdate(checkInterval); // doesn't need to await + }, + }; +}; + +module.exports = createUpdateNotifier; diff --git a/packages/strapi/package.json b/packages/strapi/package.json index b23b38c797..808dd3be89 100644 --- a/packages/strapi/package.json +++ b/packages/strapi/package.json @@ -15,11 +15,13 @@ "@koa/cors": "^3.0.0", "async": "^2.1.2", "boom": "^7.3.0", + "boxen": "4.2.0", "chalk": "^2.4.1", "chokidar": "3.3.1", "ci-info": "2.0.0", "cli-table3": "^0.6.0", "commander": "6.1.0", + "configstore": "5.0.1", "cross-spawn": "^6.0.5", "debug": "^4.1.1", "delegates": "^1.0.0", @@ -48,9 +50,11 @@ "node-schedule": "1.3.2", "opn": "^5.3.0", "ora": "^3.0.0", + "package-json": "6.5.0", "qs": "^6.9.3", "resolve-cwd": "^3.0.0", "rimraf": "^3.0.2", + "semver": "7.3.2", "strapi-database": "3.2.5", "strapi-generate": "3.2.5", "strapi-generate-api": "3.2.5", diff --git a/yarn.lock b/yarn.lock index 248eab000f..a3dc6361d0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4655,7 +4655,7 @@ bootstrap@^4.5.2: resolved "https://registry.yarnpkg.com/bootstrap/-/bootstrap-4.5.2.tgz#a85c4eda59155f0d71186b6e6ad9b875813779ab" integrity sha512-vlGn0bcySYl/iV+BGA544JkkZP5LB3jsmkeKLFQakCOwCM3AOk7VkldBz4jrzSe+Z0Ezn99NVXa1o45cQY4R6A== -boxen@^4.1.0, boxen@^4.2.0: +boxen@4.2.0, boxen@^4.1.0, boxen@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/boxen/-/boxen-4.2.0.tgz#e411b62357d6d6d36587c8ac3d5d974daa070e64" integrity sha512-eB4uT9RGzg2odpER62bBwSLvUeGC+WbRjjyyFhGsKnc8wp/m0+hQsMUvUe3H2V0D5vw0nBdO1hCJoZo5mKeuIQ== @@ -5716,6 +5716,18 @@ config-chain@^1.1.11, config-chain@^1.1.12: ini "^1.3.4" proto-list "~1.2.1" +configstore@5.0.1, configstore@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/configstore/-/configstore-5.0.1.tgz#d365021b5df4b98cdd187d6a3b0e3f6a7cc5ed96" + integrity sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA== + dependencies: + dot-prop "^5.2.0" + graceful-fs "^4.1.2" + make-dir "^3.0.0" + unique-string "^2.0.0" + write-file-atomic "^3.0.0" + xdg-basedir "^4.0.0" + configstore@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/configstore/-/configstore-4.0.0.tgz#5933311e95d3687efb592c528b922d9262d227e7" @@ -5728,18 +5740,6 @@ configstore@^4.0.0: write-file-atomic "^2.0.0" xdg-basedir "^3.0.0" -configstore@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/configstore/-/configstore-5.0.1.tgz#d365021b5df4b98cdd187d6a3b0e3f6a7cc5ed96" - integrity sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA== - dependencies: - dot-prop "^5.2.0" - graceful-fs "^4.1.2" - make-dir "^3.0.0" - unique-string "^2.0.0" - write-file-atomic "^3.0.0" - xdg-basedir "^4.0.0" - confusing-browser-globals@^1.0.9: version "1.0.9" resolved "https://registry.yarnpkg.com/confusing-browser-globals/-/confusing-browser-globals-1.0.9.tgz#72bc13b483c0276801681871d4898516f8f54fdd" @@ -13805,7 +13805,7 @@ pac-resolver@^3.0.0: netmask "^1.0.6" thunkify "^2.1.2" -package-json@^6.3.0: +package-json@6.5.0, package-json@^6.3.0: version "6.5.0" resolved "https://registry.yarnpkg.com/package-json/-/package-json-6.5.0.tgz#6feedaca35e75725876d0b0e64974697fed145b0" integrity sha512-k3bdm2n25tkyxcjSKzB5x8kfVxlMdgsbPr0GkZcwHsLpba6cBjqCt1KlcChKEvxHIcTB1FVMuwoijZ26xex5MQ== @@ -16692,16 +16692,16 @@ semver@7.0.0: resolved "https://registry.yarnpkg.com/semver/-/semver-7.0.0.tgz#5f3ca35761e47e05b206c6daff2cf814f0316b8e" integrity sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A== +semver@7.3.2, semver@^7.3.2: + version "7.3.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.2.tgz#604962b052b81ed0786aae84389ffba70ffd3938" + integrity sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ== + semver@^6.0.0, semver@^6.1.0, semver@^6.1.2, semver@^6.2.0, semver@^6.3.0: version "6.3.0" resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== -semver@^7.3.2: - version "7.3.2" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.2.tgz#604962b052b81ed0786aae84389ffba70ffd3938" - integrity sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ== - semver@~5.3.0: version "5.3.0" resolved "https://registry.yarnpkg.com/semver/-/semver-5.3.0.tgz#9b2ce5d3de02d17c6012ad326aa6b4d0cf54f94f"