feat: medusa-telemetry (#328)
* feat: adds a telemetry package to collect anonymous usage data * fix: update telemetry host * fix: adds medusa telemetry --disable * fix: add tracking of link,login,new * fix: interactively collect db credentials * fix: require seed file * fix: removes tracking from reporter
This commit is contained in:
1 parent
f07cc0fa40
commit
cfe19f7f9d
29 files changed
+6509
-58
No files matched your search
@@ -52,6 +52,7 @@
|
|||||||
"joi-objectid": "^3.0.1",
|
"joi-objectid": "^3.0.1",
|
||||||
"meant": "^1.0.1",
|
"meant": "^1.0.1",
|
||||||
"medusa-core-utils": "^0.1.27",
|
"medusa-core-utils": "^0.1.27",
|
||||||
|
"medusa-telemetry": "^0.0.1",
|
||||||
"netrc-parser": "^3.1.6",
|
"netrc-parser": "^3.1.6",
|
||||||
"open": "^8.0.6",
|
"open": "^8.0.6",
|
||||||
"ora": "^5.4.1",
|
"ora": "^5.4.1",
|
||||||
|
|||||||
@@ -3,11 +3,14 @@ const inquirer = require("inquirer")
|
|||||||
const open = require("open")
|
const open = require("open")
|
||||||
const execa = require("execa")
|
const execa = require("execa")
|
||||||
const resolveCwd = require(`resolve-cwd`)
|
const resolveCwd = require(`resolve-cwd`)
|
||||||
|
const { track } = require("medusa-telemetry")
|
||||||
|
|
||||||
const { getToken } = require("../util/token-store")
|
const { getToken } = require("../util/token-store")
|
||||||
const logger = require("../reporter").default
|
const logger = require("../reporter").default
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
link: async argv => {
|
link: async argv => {
|
||||||
|
track("CLI_LINK", { args: argv })
|
||||||
const port = process.env.PORT || 9000
|
const port = process.env.PORT || 9000
|
||||||
const appHost =
|
const appHost =
|
||||||
process.env.MEDUSA_APP_HOST || "https://app.medusa-commerce.com"
|
process.env.MEDUSA_APP_HOST || "https://app.medusa-commerce.com"
|
||||||
@@ -15,18 +18,6 @@ module.exports = {
|
|||||||
const apiHost =
|
const apiHost =
|
||||||
process.env.MEDUSA_API_HOST || "https://api.medusa-commerce.com"
|
process.env.MEDUSA_API_HOST || "https://api.medusa-commerce.com"
|
||||||
|
|
||||||
function resolveLocalCommand(command) {
|
|
||||||
try {
|
|
||||||
const cmdPath = resolveCwd.silent(
|
|
||||||
`@medusajs/medusa/dist/commands/${command}`
|
|
||||||
)
|
|
||||||
return require(cmdPath).default
|
|
||||||
} catch (err) {
|
|
||||||
console.log("Could not find local user command.")
|
|
||||||
process.exit(1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Checks if there is already a token from a previous log in; this is
|
// Checks if there is already a token from a previous log in; this is
|
||||||
// necessary to redirect the customer to the page where local linking is
|
// necessary to redirect the customer to the page where local linking is
|
||||||
// done
|
// done
|
||||||
@@ -88,6 +79,7 @@ module.exports = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
logger.success(linkActivity, "Local project linked")
|
logger.success(linkActivity, "Local project linked")
|
||||||
|
track("CLI_LINK_COMPLETED")
|
||||||
|
|
||||||
console.log()
|
console.log()
|
||||||
console.log(
|
console.log(
|
||||||
@@ -125,6 +117,8 @@ module.exports = {
|
|||||||
`Could not open browser go to: ${appHost}/local-link?lurl=http://localhost:9000<oken=${auth.user.id}`
|
`Could not open browser go to: ${appHost}/local-link?lurl=http://localhost:9000<oken=${auth.user.id}`
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
track("CLI_LINK_BROWSER_OPENED")
|
||||||
})
|
})
|
||||||
|
|
||||||
if (argv.develop) {
|
if (argv.develop) {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
const axios = require("axios").default
|
const axios = require("axios").default
|
||||||
const open = require("open")
|
const open = require("open")
|
||||||
const inquirer = require("inquirer")
|
const inquirer = require("inquirer")
|
||||||
|
const { track } = require("medusa-telemetry")
|
||||||
|
|
||||||
const logger = require("../reporter").default
|
const logger = require("../reporter").default
|
||||||
const { setToken } = require("../util/token-store")
|
const { setToken } = require("../util/token-store")
|
||||||
@@ -12,6 +13,7 @@ const { setToken } = require("../util/token-store")
|
|||||||
*/
|
*/
|
||||||
module.exports = {
|
module.exports = {
|
||||||
login: async _ => {
|
login: async _ => {
|
||||||
|
track("CLI_LOGIN")
|
||||||
const apiHost =
|
const apiHost =
|
||||||
process.env.MEDUSA_API_HOST || "https://api.medusa-commerce.com"
|
process.env.MEDUSA_API_HOST || "https://api.medusa-commerce.com"
|
||||||
|
|
||||||
@@ -80,9 +82,11 @@ module.exports = {
|
|||||||
})
|
})
|
||||||
|
|
||||||
if (user) {
|
if (user) {
|
||||||
|
track("CLI_LOGIN_SUCCEEDED")
|
||||||
logger.success(spinner, "Log in succeeded.")
|
logger.success(spinner, "Log in succeeded.")
|
||||||
setToken(auth.password)
|
setToken(auth.password)
|
||||||
} else {
|
} else {
|
||||||
|
track("CLI_LOGIN_FAILED")
|
||||||
logger.failure(spinner, "Log in failed.")
|
logger.failure(spinner, "Log in failed.")
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -10,8 +10,11 @@ import hostedGitInfo from "hosted-git-info"
|
|||||||
import isValid from "is-valid-path"
|
import isValid from "is-valid-path"
|
||||||
import sysPath from "path"
|
import sysPath from "path"
|
||||||
import prompts from "prompts"
|
import prompts from "prompts"
|
||||||
|
import { Pool } from "pg"
|
||||||
import url from "url"
|
import url from "url"
|
||||||
import { createDatabase } from "pg-god"
|
import { createDatabase } from "pg-god"
|
||||||
|
import { track } from "medusa-telemetry"
|
||||||
|
import inquirer from "inquirer"
|
||||||
|
|
||||||
import reporter from "../reporter"
|
import reporter from "../reporter"
|
||||||
import { getPackageManager, setPackageManager } from "../util/package-manager"
|
import { getPackageManager, setPackageManager } from "../util/package-manager"
|
||||||
@@ -76,7 +79,7 @@ const createInitialGitCommit = async (rootPath, starterUrl) => {
|
|||||||
// use execSync instead of spawn to handle git clients using
|
// use execSync instead of spawn to handle git clients using
|
||||||
// pgp signatures (with password)
|
// pgp signatures (with password)
|
||||||
try {
|
try {
|
||||||
execSync(`git commit -m "Initial commit from gatsby: (${starterUrl})"`, {
|
execSync(`git commit -m "Initial commit from medusa: (${starterUrl})"`, {
|
||||||
cwd: rootPath,
|
cwd: rootPath,
|
||||||
})
|
})
|
||||||
} catch {
|
} catch {
|
||||||
@@ -91,6 +94,8 @@ const install = async rootPath => {
|
|||||||
const prevDir = process.cwd()
|
const prevDir = process.cwd()
|
||||||
|
|
||||||
reporter.info(`Installing packages...`)
|
reporter.info(`Installing packages...`)
|
||||||
|
console.log() // Add some space
|
||||||
|
|
||||||
process.chdir(rootPath)
|
process.chdir(rootPath)
|
||||||
|
|
||||||
const npmConfigUserAgent = process.env.npm_config_user_agent
|
const npmConfigUserAgent = process.env.npm_config_user_agent
|
||||||
@@ -145,6 +150,7 @@ const copy = async (starterPath, rootPath) => {
|
|||||||
await fs.copy(starterPath, rootPath, { filter: ignored })
|
await fs.copy(starterPath, rootPath, { filter: ignored })
|
||||||
|
|
||||||
reporter.success(copyActivity, `Created starter directory layout`)
|
reporter.success(copyActivity, `Created starter directory layout`)
|
||||||
|
console.log() // Add some space
|
||||||
|
|
||||||
await install(rootPath)
|
await install(rootPath)
|
||||||
|
|
||||||
@@ -242,8 +248,122 @@ const defaultDBCreds = {
|
|||||||
host: "localhost",
|
host: "localhost",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const verifyPgCreds = async creds => {
|
||||||
|
const pool = new Pool(creds)
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
pool.query("SELECT NOW()", (err, res) => {
|
||||||
|
pool.end()
|
||||||
|
if (err) {
|
||||||
|
reject(err)
|
||||||
|
} else {
|
||||||
|
resolve(res)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const interactiveDbCreds = async (dbName, dbCreds = {}) => {
|
||||||
|
const credentials = Object.assign({}, defaultDBCreds, dbCreds)
|
||||||
|
let collecting = true
|
||||||
|
while (collecting) {
|
||||||
|
const result = await inquirer
|
||||||
|
.prompt([
|
||||||
|
{
|
||||||
|
type: "list",
|
||||||
|
name: "continueWithDefault",
|
||||||
|
message: `
|
||||||
|
|
||||||
|
Will attempt to setup database "${dbName}" with credentials:
|
||||||
|
user: ${credentials.user}
|
||||||
|
password: ***
|
||||||
|
database: ${credentials.database}
|
||||||
|
port: ${credentials.port}
|
||||||
|
host: ${credentials.host}
|
||||||
|
Do you wish to continue with these credentials?
|
||||||
|
|
||||||
|
`,
|
||||||
|
choices: [`Continue`, `Change credentials`, `Skip database setup`],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "input",
|
||||||
|
when: ({ continueWithDefault }) =>
|
||||||
|
continueWithDefault === `Change credentials`,
|
||||||
|
name: "user",
|
||||||
|
default: credentials.user,
|
||||||
|
message: `DB user`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "password",
|
||||||
|
when: ({ continueWithDefault }) =>
|
||||||
|
continueWithDefault === `Change credentials`,
|
||||||
|
name: "password",
|
||||||
|
default: credentials.password,
|
||||||
|
message: `DB password`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "number",
|
||||||
|
when: ({ continueWithDefault }) =>
|
||||||
|
continueWithDefault === `Change credentials`,
|
||||||
|
name: "port",
|
||||||
|
default: credentials.port,
|
||||||
|
message: `DB port`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "input",
|
||||||
|
when: ({ continueWithDefault }) =>
|
||||||
|
continueWithDefault === `Change credentials`,
|
||||||
|
name: "host",
|
||||||
|
default: credentials.host,
|
||||||
|
message: `DB host`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "input",
|
||||||
|
when: ({ continueWithDefault }) =>
|
||||||
|
continueWithDefault === `Change credentials`,
|
||||||
|
name: "database",
|
||||||
|
default: credentials.database,
|
||||||
|
message: `DB database`,
|
||||||
|
},
|
||||||
|
])
|
||||||
|
.then(async answers => {
|
||||||
|
const collectedCreds = Object.assign({}, credentials, {
|
||||||
|
user: answers.user,
|
||||||
|
password: answers.password,
|
||||||
|
host: answers.host,
|
||||||
|
port: answers.port,
|
||||||
|
database: answers.database,
|
||||||
|
})
|
||||||
|
|
||||||
|
switch (answers.continueWithDefault) {
|
||||||
|
case "Continue": {
|
||||||
|
const done = await verifyPgCreds(credentials).catch(_ => false)
|
||||||
|
if (done) {
|
||||||
|
return credentials
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
case "Change credentials": {
|
||||||
|
const done = await verifyPgCreds(collectedCreds).catch(_ => false)
|
||||||
|
if (done) {
|
||||||
|
return collectedCreds
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
if (result !== false) {
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log("\n\nCould not verify DB credentials - please try again\n\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const setupDB = async (dbName, dbCreds = {}) => {
|
const setupDB = async (dbName, dbCreds = {}) => {
|
||||||
const credentials = Object.assign(defaultDBCreds, dbCreds)
|
const credentials = Object.assign({}, defaultDBCreds, dbCreds)
|
||||||
|
|
||||||
const dbActivity = reporter.activity(`Setting up database "${dbName}"...`)
|
const dbActivity = reporter.activity(`Setting up database "${dbName}"...`)
|
||||||
await createDatabase(
|
await createDatabase(
|
||||||
@@ -257,7 +377,7 @@ const setupDB = async (dbName, dbCreds = {}) => {
|
|||||||
reporter.success(dbActivity, `Created database "${dbName}"`)
|
reporter.success(dbActivity, `Created database "${dbName}"`)
|
||||||
})
|
})
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
if ((err.name = "PDG_ERR::DuplicateDatabase")) {
|
if (err.name === "PDG_ERR::DuplicateDatabase") {
|
||||||
reporter.success(
|
reporter.success(
|
||||||
dbActivity,
|
dbActivity,
|
||||||
`Database ${dbName} already exists; skipping setup`
|
`Database ${dbName} already exists; skipping setup`
|
||||||
@@ -273,23 +393,26 @@ const setupDB = async (dbName, dbCreds = {}) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const setupEnvVars = async (rootPath, dbName, dbCreds = {}) => {
|
const setupEnvVars = async (rootPath, dbName, dbCreds = {}) => {
|
||||||
const credentials = Object.assign(defaultDBCreds, dbCreds)
|
const credentials = Object.assign({}, defaultDBCreds, dbCreds)
|
||||||
|
|
||||||
|
let dbUrl = ""
|
||||||
|
if (
|
||||||
|
credentials.user !== defaultDBCreds.user ||
|
||||||
|
credentials.password !== defaultDBCreds.password
|
||||||
|
) {
|
||||||
|
dbUrl = `postgres://${credentials.user}:${credentials.password}@${credentials.host}:${credentials.port}/${dbName}`
|
||||||
|
} else {
|
||||||
|
dbUrl = `postgres://${credentials.host}:${credentials.port}/${dbName}`
|
||||||
|
}
|
||||||
|
|
||||||
const templatePath = sysPath.join(rootPath, ".env.template")
|
const templatePath = sysPath.join(rootPath, ".env.template")
|
||||||
const destination = sysPath.join(rootPath, ".env")
|
const destination = sysPath.join(rootPath, ".env")
|
||||||
if (existsSync(templatePath)) {
|
if (existsSync(templatePath)) {
|
||||||
fs.renameSync(templatePath, destination)
|
fs.renameSync(templatePath, destination)
|
||||||
fs.appendFileSync(
|
|
||||||
destination,
|
|
||||||
`DATABASE_URL=postgres://${credentials.user}:${credentials.password}@${credentials.host}:${credentials.port}/${dbName}\n`
|
|
||||||
)
|
|
||||||
} else {
|
} else {
|
||||||
reporter.info(`No .env.template found. Creating .env.`)
|
reporter.info(`No .env.template found. Creating .env.`)
|
||||||
fs.appendFileSync(
|
|
||||||
destination,
|
|
||||||
`DATABASE_URL=postgres://${credentials.user}:${credentials.password}@${credentials.host}:${credentials.port}/${dbName}\n`
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
fs.appendFileSync(destination, `DATABASE_URL=${dbUrl}\n`)
|
||||||
}
|
}
|
||||||
|
|
||||||
const runMigrations = async rootPath => {
|
const runMigrations = async rootPath => {
|
||||||
@@ -354,6 +477,8 @@ const attemptSeed = async rootPath => {
|
|||||||
* Main function that clones or copies the starter.
|
* Main function that clones or copies the starter.
|
||||||
*/
|
*/
|
||||||
export const newStarter = async args => {
|
export const newStarter = async args => {
|
||||||
|
track("CLI_NEW")
|
||||||
|
|
||||||
const {
|
const {
|
||||||
starter,
|
starter,
|
||||||
root,
|
root,
|
||||||
@@ -361,6 +486,7 @@ export const newStarter = async args => {
|
|||||||
skipMigrations,
|
skipMigrations,
|
||||||
skipEnv,
|
skipEnv,
|
||||||
seed,
|
seed,
|
||||||
|
useDefaults,
|
||||||
dbUser,
|
dbUser,
|
||||||
dbDatabase,
|
dbDatabase,
|
||||||
dbPass,
|
dbPass,
|
||||||
@@ -430,21 +556,38 @@ export const newStarter = async args => {
|
|||||||
await copy(starterPath, rootPath)
|
await copy(starterPath, rootPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!skipDb) {
|
track("CLI_NEW_LAYOUT_COMPLETED")
|
||||||
await setupDB(root, dbCredentials)
|
|
||||||
|
let creds = dbCredentials
|
||||||
|
|
||||||
|
if (!useDefaults && !skipDb && !skipEnv) {
|
||||||
|
creds = await interactiveDbCreds(root, dbCredentials)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!skipEnv) {
|
if (creds === null) {
|
||||||
await setupEnvVars(rootPath, root, dbCredentials)
|
reporter.info("Skipping automatic database setup")
|
||||||
}
|
} else {
|
||||||
|
if (!skipDb) {
|
||||||
|
track("CLI_NEW_SETUP_DB")
|
||||||
|
await setupDB(root, creds)
|
||||||
|
}
|
||||||
|
|
||||||
if (!skipMigrations) {
|
if (!skipEnv) {
|
||||||
await runMigrations(rootPath)
|
track("CLI_NEW_SETUP_ENV")
|
||||||
}
|
await setupEnvVars(rootPath, root, creds)
|
||||||
|
}
|
||||||
|
|
||||||
if (seed) {
|
if (!skipMigrations) {
|
||||||
await attemptSeed(rootPath)
|
track("CLI_NEW_RUN_MIGRATIONS")
|
||||||
|
await runMigrations(rootPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (seed) {
|
||||||
|
track("CLI_NEW_SEED_DB")
|
||||||
|
await attemptSeed(rootPath)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
successMessage(rootPath)
|
successMessage(rootPath)
|
||||||
|
track("CLI_NEW_SUCCEEDED")
|
||||||
}
|
}
|
||||||
@@ -2,10 +2,12 @@ const path = require(`path`)
|
|||||||
const resolveCwd = require(`resolve-cwd`)
|
const resolveCwd = require(`resolve-cwd`)
|
||||||
const yargs = require(`yargs`)
|
const yargs = require(`yargs`)
|
||||||
const existsSync = require(`fs-exists-cached`).sync
|
const existsSync = require(`fs-exists-cached`).sync
|
||||||
|
const { setTelemetryEnabled } = require("medusa-telemetry")
|
||||||
|
|
||||||
const { getLocalMedusaVersion } = require(`./util/version`)
|
const { getLocalMedusaVersion } = require(`./util/version`)
|
||||||
const { didYouMean } = require(`./did-you-mean`)
|
const { didYouMean } = require(`./did-you-mean`)
|
||||||
|
|
||||||
|
const reporter = require("./reporter").default
|
||||||
const { newStarter } = require("./commands/new")
|
const { newStarter } = require("./commands/new")
|
||||||
const { whoami } = require("./commands/whoami")
|
const { whoami } = require("./commands/whoami")
|
||||||
const { login } = require("./commands/login")
|
const { login } = require("./commands/login")
|
||||||
@@ -61,7 +63,6 @@ function buildLocalCommands(cli, isLocalProject) {
|
|||||||
const localCmd = resolveLocalCommand(command)
|
const localCmd = resolveLocalCommand(command)
|
||||||
const args = { ...argv, ...projectInfo, useYarn }
|
const args = { ...argv, ...projectInfo, useYarn }
|
||||||
|
|
||||||
// report.verbose(`running command: ${command}`)
|
|
||||||
return handler ? handler(args, localCmd) : localCmd(args)
|
return handler ? handler(args, localCmd) : localCmd(args)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -75,6 +76,12 @@ function buildLocalCommands(cli, isLocalProject) {
|
|||||||
describe: `If flag is set the command will attempt to seed the database after setup.`,
|
describe: `If flag is set the command will attempt to seed the database after setup.`,
|
||||||
default: false,
|
default: false,
|
||||||
})
|
})
|
||||||
|
.option(`y`, {
|
||||||
|
type: `boolean`,
|
||||||
|
alias: "useDefaults",
|
||||||
|
describe: `If flag is set the command will not interactively collect database credentials`,
|
||||||
|
default: false,
|
||||||
|
})
|
||||||
.option(`skip-db`, {
|
.option(`skip-db`, {
|
||||||
type: `boolean`,
|
type: `boolean`,
|
||||||
describe: `If flag is set the command will not attempt to complete database setup`,
|
describe: `If flag is set the command will not attempt to complete database setup`,
|
||||||
@@ -118,6 +125,28 @@ function buildLocalCommands(cli, isLocalProject) {
|
|||||||
desc: `Create a new Medusa project.`,
|
desc: `Create a new Medusa project.`,
|
||||||
handler: handlerP(newStarter),
|
handler: handlerP(newStarter),
|
||||||
})
|
})
|
||||||
|
.command({
|
||||||
|
command: `telemetry`,
|
||||||
|
describe: `Enable or disable collection of anonymous usage data.`,
|
||||||
|
builder: yargs =>
|
||||||
|
yargs
|
||||||
|
.option(`enable`, {
|
||||||
|
type: `boolean`,
|
||||||
|
description: `Enable telemetry (default)`,
|
||||||
|
})
|
||||||
|
.option(`disable`, {
|
||||||
|
type: `boolean`,
|
||||||
|
description: `Disable telemetry`,
|
||||||
|
}),
|
||||||
|
|
||||||
|
handler: handlerP(({ enable, disable }) => {
|
||||||
|
const enabled = Boolean(enable) || !disable
|
||||||
|
setTelemetryEnabled(enabled)
|
||||||
|
reporter.info(
|
||||||
|
`Telemetry collection ${enabled ? `enabled` : `disabled`}`
|
||||||
|
)
|
||||||
|
}),
|
||||||
|
})
|
||||||
.command({
|
.command({
|
||||||
command: `seed`,
|
command: `seed`,
|
||||||
desc: `Migrates and populates the database with the provided file.`,
|
desc: `Migrates and populates the database with the provided file.`,
|
||||||
@@ -126,6 +155,7 @@ function buildLocalCommands(cli, isLocalProject) {
|
|||||||
alias: `seed-file`,
|
alias: `seed-file`,
|
||||||
type: `string`,
|
type: `string`,
|
||||||
describe: `Path to the file where the seed is defined.`,
|
describe: `Path to the file where the seed is defined.`,
|
||||||
|
required: true,
|
||||||
}).option(`m`, {
|
}).option(`m`, {
|
||||||
alias: `migrate`,
|
alias: `migrate`,
|
||||||
type: `boolean`,
|
type: `boolean`,
|
||||||
@@ -364,8 +394,8 @@ module.exports = argv => {
|
|||||||
const suggestion = arg ? didYouMean(arg, availableCommands) : ``
|
const suggestion = arg ? didYouMean(arg, availableCommands) : ``
|
||||||
|
|
||||||
cli.showHelp()
|
cli.showHelp()
|
||||||
// report.log(suggestion)
|
reporter.info(suggestion)
|
||||||
// report.log(msg)
|
reporter.info(msg)
|
||||||
})
|
})
|
||||||
.parse(argv.slice(2))
|
.parse(argv.slice(2))
|
||||||
}
|
}
|
||||||
@@ -2,6 +2,7 @@ import stackTrace from "stack-trace"
|
|||||||
import { ulid } from "ulid"
|
import { ulid } from "ulid"
|
||||||
import winston from "winston"
|
import winston from "winston"
|
||||||
import ora from "ora"
|
import ora from "ora"
|
||||||
|
import { track } from "medusa-telemetry"
|
||||||
|
|
||||||
const LOG_LEVEL = process.env.LOG_LEVEL || "silly"
|
const LOG_LEVEL = process.env.LOG_LEVEL || "silly"
|
||||||
const NODE_ENV = process.env.NODE_ENV || "development"
|
const NODE_ENV = process.env.NODE_ENV || "development"
|
||||||
@@ -41,11 +42,17 @@ export class Reporter {
|
|||||||
this.ora_ = activityLogger
|
this.ora_ = activityLogger
|
||||||
}
|
}
|
||||||
|
|
||||||
panic = error => {
|
panic = data => {
|
||||||
this.loggerInstance_.log({
|
this.loggerInstance_.log({
|
||||||
level: "error",
|
level: "error",
|
||||||
details: error,
|
details: data,
|
||||||
|
message: data.error && data.error.message,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
track("PANIC_ERROR_REACHED", {
|
||||||
|
id: data.id,
|
||||||
|
})
|
||||||
|
|
||||||
process.exit(1)
|
process.exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -83,13 +90,14 @@ export class Reporter {
|
|||||||
* @returns {string} the id of the activity; this should be passed to do
|
* @returns {string} the id of the activity; this should be passed to do
|
||||||
* further operations on the activity such as success, failure, progress.
|
* further operations on the activity such as success, failure, progress.
|
||||||
*/
|
*/
|
||||||
activity = message => {
|
activity = (message, config = {}) => {
|
||||||
const id = ulid()
|
const id = ulid()
|
||||||
if (NODE_ENV === "development" && this.shouldLog("info")) {
|
if (NODE_ENV === "development" && this.shouldLog("info")) {
|
||||||
const activity = this.ora_(message).start()
|
const activity = this.ora_(message).start()
|
||||||
|
|
||||||
this.activities_[id] = {
|
this.activities_[id] = {
|
||||||
activity,
|
activity,
|
||||||
|
config,
|
||||||
start: Date.now(),
|
start: Date.now(),
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -97,10 +105,12 @@ export class Reporter {
|
|||||||
} else {
|
} else {
|
||||||
this.activities_[id] = {
|
this.activities_[id] = {
|
||||||
start: Date.now(),
|
start: Date.now(),
|
||||||
|
config,
|
||||||
}
|
}
|
||||||
this.loggerInstance_.log({
|
this.loggerInstance_.log({
|
||||||
activity_id: id,
|
activity_id: id,
|
||||||
level: "info",
|
level: "info",
|
||||||
|
config,
|
||||||
message,
|
message,
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -166,15 +176,16 @@ export class Reporter {
|
|||||||
* at the error level.
|
* at the error level.
|
||||||
* @param {string} activityId - the id of the activity as returned by activity
|
* @param {string} activityId - the id of the activity as returned by activity
|
||||||
* @param {string} message - the message to log
|
* @param {string} message - the message to log
|
||||||
|
* @returns {object} data about the activity
|
||||||
*/
|
*/
|
||||||
failure = (activityId, message) => {
|
failure = (activityId, message) => {
|
||||||
|
const time = Date.now()
|
||||||
const toLog = {
|
const toLog = {
|
||||||
level: "error",
|
level: "error",
|
||||||
message,
|
message,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof activityId === "string" && this.activities_[activityId]) {
|
if (typeof activityId === "string" && this.activities_[activityId]) {
|
||||||
const time = Date.now()
|
|
||||||
const activity = this.activities_[activityId]
|
const activity = this.activities_[activityId]
|
||||||
if (activity.activity) {
|
if (activity.activity) {
|
||||||
activity.activity.fail(`${message} – ${time - activity.start}`)
|
activity.activity.fail(`${message} – ${time - activity.start}`)
|
||||||
@@ -186,6 +197,16 @@ export class Reporter {
|
|||||||
} else {
|
} else {
|
||||||
this.loggerInstance_.log(toLog)
|
this.loggerInstance_.log(toLog)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.activities_[activityId]) {
|
||||||
|
const activity = this.activities_[activityId]
|
||||||
|
return {
|
||||||
|
...activity,
|
||||||
|
duration: time - activity.start,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -194,15 +215,16 @@ export class Reporter {
|
|||||||
* at the info level.
|
* at the info level.
|
||||||
* @param {string} activityId - the id of the activity as returned by activity
|
* @param {string} activityId - the id of the activity as returned by activity
|
||||||
* @param {string} message - the message to log
|
* @param {string} message - the message to log
|
||||||
|
* @returns {object} data about the activity
|
||||||
*/
|
*/
|
||||||
success = (activityId, message) => {
|
success = (activityId, message) => {
|
||||||
|
const time = Date.now()
|
||||||
const toLog = {
|
const toLog = {
|
||||||
level: "info",
|
level: "info",
|
||||||
message,
|
message,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof activityId === "string" && this.activities_[activityId]) {
|
if (typeof activityId === "string" && this.activities_[activityId]) {
|
||||||
const time = Date.now()
|
|
||||||
const activity = this.activities_[activityId]
|
const activity = this.activities_[activityId]
|
||||||
if (activity.activity) {
|
if (activity.activity) {
|
||||||
activity.activity.succeed(`${message} – ${time - activity.start}ms`)
|
activity.activity.succeed(`${message} – ${time - activity.start}ms`)
|
||||||
@@ -214,6 +236,16 @@ export class Reporter {
|
|||||||
} else {
|
} else {
|
||||||
this.loggerInstance_.log(toLog)
|
this.loggerInstance_.log(toLog)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.activities_[activityId]) {
|
||||||
|
const activity = this.activities_[activityId]
|
||||||
|
return {
|
||||||
|
...activity,
|
||||||
|
duration: time - activity.start,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
let ignore = [`**/dist`]
|
||||||
|
|
||||||
|
// Jest needs to compile this code, but generally we don't want this copied
|
||||||
|
// to output folders
|
||||||
|
if (process.env.NODE_ENV !== `test`) {
|
||||||
|
ignore.push(`**/__tests__`)
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
presets: [["babel-preset-medusa-package"], ["@babel/preset-typescript"]],
|
||||||
|
ignore,
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"plugins": ["prettier"],
|
||||||
|
"extends": ["prettier"],
|
||||||
|
"rules": {
|
||||||
|
"prettier/prettier": "error",
|
||||||
|
"semi": "error",
|
||||||
|
"no-unused-expressions": "true"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
/dist
|
||||||
|
node_modules
|
||||||
|
.DS_store
|
||||||
|
.env*
|
||||||
|
.env
|
||||||
|
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
src
|
||||||
|
.prettierrc
|
||||||
|
.env
|
||||||
|
.babelrc.js
|
||||||
|
.eslintrc
|
||||||
|
.gitignore
|
||||||
|
ormconfig.json
|
||||||
|
tsconfig.json
|
||||||
|
jest.config.md
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"endOfLine": "lf",
|
||||||
|
"semi": false,
|
||||||
|
"singleQuote": false,
|
||||||
|
"tabWidth": 2,
|
||||||
|
"trailingComma": "es5"
|
||||||
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
{
|
||||||
|
"name": "medusa-telemetry",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"description": "Telemetry for Medusa",
|
||||||
|
"main": "dist/index.js",
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/medusajs/medusa",
|
||||||
|
"directory": "packages/medusa-telemetry"
|
||||||
|
},
|
||||||
|
"publishConfig": {
|
||||||
|
"access": "public"
|
||||||
|
},
|
||||||
|
"author": "Sebastian Rindom",
|
||||||
|
"license": "MIT",
|
||||||
|
"devDependencies": {
|
||||||
|
"@babel/cli": "^7.14.3",
|
||||||
|
"@babel/core": "^7.14.3",
|
||||||
|
"@babel/preset-typescript": "^7.13.0",
|
||||||
|
"babel-preset-medusa-package": "^1.1.13",
|
||||||
|
"cross-env": "^5.2.1",
|
||||||
|
"eslint": "^6.8.0",
|
||||||
|
"jest": "^25.5.2",
|
||||||
|
"nodemon": "^2.0.1",
|
||||||
|
"prettier": "^1.19.1"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"start": "nodemon --watch plugins/ --watch src/ --exec babel-node src/app.js",
|
||||||
|
"watch": "babel -w src --out-dir dist/ --ignore **/__tests__ --extensions \".ts,.js\"",
|
||||||
|
"prepare": "cross-env NODE_ENV=production npm run build",
|
||||||
|
"build": "babel src -d dist --ignore **/__tests__ --extensions \".ts,.js\"",
|
||||||
|
"serve": "node dist/app.js",
|
||||||
|
"postinstall": "node dist/postinstall.js || true",
|
||||||
|
"test": "jest",
|
||||||
|
"test:unit": "jest"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"axios": "^0.21.1",
|
||||||
|
"axios-retry": "^3.1.9",
|
||||||
|
"boxen": "^5.0.1",
|
||||||
|
"configstore": "5.0.1",
|
||||||
|
"is-docker": "^2.2.1",
|
||||||
|
"remove-trailing-slash": "^0.1.1",
|
||||||
|
"uuid": "^8.3.2"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
import Telemeter from "./telemeter"
|
||||||
|
import createFlush from "./util/create-flush"
|
||||||
|
|
||||||
|
const telemeter = new Telemeter()
|
||||||
|
|
||||||
|
export const flush = createFlush(telemeter.isTrackingEnabled())
|
||||||
|
|
||||||
|
if (flush) {
|
||||||
|
process.on(`exit`, flush)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const track = (event, data = {}) => {
|
||||||
|
telemeter.track(event, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const setTelemetryEnabled = (enabled = true) => {
|
||||||
|
telemeter.setTelemetryEnabled(enabled)
|
||||||
|
}
|
||||||
|
|
||||||
|
export { default as Telemeter } from "./telemeter"
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
try {
|
||||||
|
const showAnalyticsNotification = require(`./util/show-notification`)
|
||||||
|
const Store = require(`./store`)
|
||||||
|
|
||||||
|
const eventStorage = new Store()
|
||||||
|
const disabled = eventStorage.disabled_
|
||||||
|
const enabledInConfig = eventStorage.getConfig(`telemetry.enabled`)
|
||||||
|
if (enabledInConfig === undefined && !disabled) {
|
||||||
|
showAnalyticsNotification()
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
import path from "path"
|
||||||
|
import Configstore from "configstore"
|
||||||
|
|
||||||
|
import InMemConfig from "./util/in-memory-config"
|
||||||
|
import OutboxStore from "./util/outbox-store"
|
||||||
|
import isTruthy from "./util/is-truthy"
|
||||||
|
|
||||||
|
class Store {
|
||||||
|
constructor() {
|
||||||
|
try {
|
||||||
|
this.config_ = new Configstore(`medusa`, {}, { globalConfigPath: true })
|
||||||
|
} catch (e) {
|
||||||
|
this.config_ = new InMemConfig()
|
||||||
|
}
|
||||||
|
|
||||||
|
const baseDir = path.dirname(this.config_.path)
|
||||||
|
this.outbox_ = new OutboxStore(baseDir)
|
||||||
|
|
||||||
|
this.disabled_ = isTruthy(process.env.MEDUSA_DISABLE_TELEMETRY)
|
||||||
|
}
|
||||||
|
|
||||||
|
getQueueSize() {
|
||||||
|
return this.outbox_.getSize()
|
||||||
|
}
|
||||||
|
|
||||||
|
getQueueCount() {
|
||||||
|
return this.outbox_.getCount()
|
||||||
|
}
|
||||||
|
|
||||||
|
addEvent(event) {
|
||||||
|
if (this.disabled_) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const eventString = JSON.stringify(event)
|
||||||
|
return this.outbox_.appendToBuffer(eventString + `\n`)
|
||||||
|
}
|
||||||
|
|
||||||
|
async flushEvents(handler) {
|
||||||
|
return await this.outbox_.startFlushEvents(async eventData => {
|
||||||
|
const events = eventData
|
||||||
|
.split(`\n`)
|
||||||
|
.filter(e => e && e.length > 2)
|
||||||
|
.map(e => JSON.parse(e))
|
||||||
|
|
||||||
|
return await handler(events)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
getConfig(path) {
|
||||||
|
return this.config_.get(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
setConfig(path, val) {
|
||||||
|
return this.config_.set(path, val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Store
|
||||||
@@ -0,0 +1,152 @@
|
|||||||
|
import os from "os"
|
||||||
|
import fs from "fs"
|
||||||
|
import { join, sep } from "path"
|
||||||
|
import isDocker from "is-docker"
|
||||||
|
import { v4 as uuidv4 } from "uuid"
|
||||||
|
|
||||||
|
import createFlush from "./util/create-flush"
|
||||||
|
import showAnalyticsNotification from "./util/show-notification"
|
||||||
|
import isTruthy from "./util/is-truthy"
|
||||||
|
import getTermProgram from "./util/get-term-program"
|
||||||
|
import Store from "./store"
|
||||||
|
|
||||||
|
const MEDUSA_TELEMETRY_VERBOSE = process.env.MEDUSA_TELEMETRY_VERBOSE || false
|
||||||
|
|
||||||
|
class Telemeter {
|
||||||
|
constructor(options = {}) {
|
||||||
|
this.store_ = new Store()
|
||||||
|
|
||||||
|
this.flushAt = Math.max(options.flushAt, 1) || 20
|
||||||
|
this.maxQueueSize = options.maxQueueSize || 1024 * 500
|
||||||
|
this.flushInterval = options.flushInterval || 10 * 1000
|
||||||
|
this.flushed = false
|
||||||
|
|
||||||
|
this.queueSize_ = this.store_.getQueueSize()
|
||||||
|
this.queueCount_ = this.store_.getQueueCount()
|
||||||
|
}
|
||||||
|
|
||||||
|
getMachineId() {
|
||||||
|
if (this.machineId) {
|
||||||
|
return this.machineId
|
||||||
|
}
|
||||||
|
let machineId = this.store_.getConfig(`telemetry.machine_id`)
|
||||||
|
if (typeof machineId !== `string`) {
|
||||||
|
machineId = uuidv4()
|
||||||
|
this.store_.setConfig(`telemetry.machine_id`, machineId)
|
||||||
|
}
|
||||||
|
this.machineId = machineId
|
||||||
|
return machineId
|
||||||
|
}
|
||||||
|
|
||||||
|
isTrackingEnabled() {
|
||||||
|
// Cache the result
|
||||||
|
if (this.trackingEnabled !== undefined) {
|
||||||
|
return this.trackingEnabled
|
||||||
|
}
|
||||||
|
let enabled = this.store_.getConfig(`telemetry.enabled`)
|
||||||
|
if (enabled === undefined || enabled === null) {
|
||||||
|
showAnalyticsNotification()
|
||||||
|
enabled = true
|
||||||
|
this.store_.setConfig(`telemetry.enabled`, enabled)
|
||||||
|
}
|
||||||
|
this.trackingEnabled = enabled
|
||||||
|
return enabled
|
||||||
|
}
|
||||||
|
|
||||||
|
getOsInfo() {
|
||||||
|
if (this.osInfo) {
|
||||||
|
return this.osInfo
|
||||||
|
}
|
||||||
|
const cpus = os.cpus()
|
||||||
|
const osInfo = {
|
||||||
|
node_version: process.version,
|
||||||
|
platform: os.platform(),
|
||||||
|
release: os.release(),
|
||||||
|
cpus: (cpus && cpus.length > 0 && cpus[0].model) || undefined,
|
||||||
|
arch: os.arch(),
|
||||||
|
docker: isDocker(),
|
||||||
|
term_program: getTermProgram(),
|
||||||
|
}
|
||||||
|
this.osInfo = osInfo
|
||||||
|
return osInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
getMedusaVersion() {
|
||||||
|
try {
|
||||||
|
const packageJson = require.resolve(`@medusajs/medusa/package.json`)
|
||||||
|
const { version } = JSON.parse(fs.readFileSync(packageJson, `utf-8`))
|
||||||
|
return version
|
||||||
|
} catch (e) {
|
||||||
|
if (isTruthy(MEDUSA_TELEMETRY_VERBOSE)) {
|
||||||
|
console.error("failed to get medusa version", e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return `-0.0.0`
|
||||||
|
}
|
||||||
|
|
||||||
|
getCliVersion() {
|
||||||
|
try {
|
||||||
|
const jsonfile = join(
|
||||||
|
require
|
||||||
|
.resolve(`@medusajs/medusa-cli`) // Resolve where current gatsby-cli would be loaded from.
|
||||||
|
.split(sep)
|
||||||
|
.slice(0, -2) // drop lib/index.js
|
||||||
|
.join(sep),
|
||||||
|
`package.json`
|
||||||
|
)
|
||||||
|
|
||||||
|
const { version } = require(jsonfile)
|
||||||
|
return version
|
||||||
|
} catch (e) {
|
||||||
|
if (isTruthy(MEDUSA_TELEMETRY_VERBOSE)) {
|
||||||
|
console.error("failed to get medusa version", e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return `-0.0.0`
|
||||||
|
}
|
||||||
|
|
||||||
|
setTelemetryEnabled(enabled) {
|
||||||
|
this.trackingEnabled = enabled
|
||||||
|
this.store_.setConfig(`telemetry.enabled`, enabled)
|
||||||
|
}
|
||||||
|
|
||||||
|
track(event, data) {
|
||||||
|
return this.enqueue_(event, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
enqueue_(type, data) {
|
||||||
|
const event = {
|
||||||
|
id: `te_${uuidv4()}`,
|
||||||
|
type,
|
||||||
|
properties: data,
|
||||||
|
timestamp: new Date(),
|
||||||
|
machine_id: this.getMachineId(),
|
||||||
|
os_info: this.getOsInfo(),
|
||||||
|
medusa_version: this.getMedusaVersion(),
|
||||||
|
cli_version: this.getCliVersion(),
|
||||||
|
}
|
||||||
|
|
||||||
|
this.store_.addEvent(event)
|
||||||
|
|
||||||
|
this.queueCount_ += 1
|
||||||
|
this.queueSize_ += JSON.stringify(event).length
|
||||||
|
|
||||||
|
const hasReachedFlushAt = this.queueCount_ >= this.flushAt
|
||||||
|
const hasReachedQueueSize = this.queueSize_ >= this.maxQueueSize
|
||||||
|
|
||||||
|
if (hasReachedQueueSize || hasReachedFlushAt) {
|
||||||
|
const flush = createFlush(this.isTrackingEnabled())
|
||||||
|
flush && flush()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.flushInterval && !this.timer) {
|
||||||
|
const flush = createFlush(this.isTrackingEnabled())
|
||||||
|
if (flush) {
|
||||||
|
this.timer = setTimeout(flush, this.flushInterval)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Telemeter
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
import { join } from "path"
|
||||||
|
import { fork } from "child_process"
|
||||||
|
import isTruthy from "./is-truthy"
|
||||||
|
|
||||||
|
const MEDUSA_TELEMETRY_VERBOSE = process.env.MEDUSA_TELEMETRY_VERBOSE || false
|
||||||
|
|
||||||
|
function createFlush(enabled) {
|
||||||
|
if (!enabled) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return async function flush() {
|
||||||
|
if (isTruthy(MEDUSA_TELEMETRY_VERBOSE)) {
|
||||||
|
console.log("Flushing queue...")
|
||||||
|
}
|
||||||
|
|
||||||
|
const forked = fork(join(__dirname, `send.js`), {
|
||||||
|
detached: true,
|
||||||
|
stdio: MEDUSA_TELEMETRY_VERBOSE ? `inherit` : `ignore`,
|
||||||
|
execArgv: [],
|
||||||
|
})
|
||||||
|
forked.unref()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default createFlush
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
function getTermProgram() {
|
||||||
|
const { TERM_PROGRAM, WT_SESSION } = process.env
|
||||||
|
if (TERM_PROGRAM) {
|
||||||
|
return TERM_PROGRAM
|
||||||
|
} else if (WT_SESSION) {
|
||||||
|
// https://github.com/microsoft/terminal/issues/1040
|
||||||
|
return `WindowsTerminal`
|
||||||
|
}
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
export default getTermProgram
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
import { v4 as uuidv4 } from "uuid"
|
||||||
|
import os from "os"
|
||||||
|
import { join } from "path"
|
||||||
|
|
||||||
|
export class InMemoryConfigStore {
|
||||||
|
config = {}
|
||||||
|
path = join(os.tmpdir(), `medusa`)
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.config = this.createBaseConfig()
|
||||||
|
}
|
||||||
|
|
||||||
|
createBaseConfig() {
|
||||||
|
return {
|
||||||
|
"telemetry.enabled": true,
|
||||||
|
"telemetry.machine_id": `not-a-machine-id-${uuidv4()}`,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
get(key) {
|
||||||
|
return this.config[key]
|
||||||
|
}
|
||||||
|
|
||||||
|
set(key, value) {
|
||||||
|
this.config[key] = value
|
||||||
|
}
|
||||||
|
|
||||||
|
all() {
|
||||||
|
return this.config
|
||||||
|
}
|
||||||
|
|
||||||
|
size() {
|
||||||
|
return Object.keys(this.config).length
|
||||||
|
}
|
||||||
|
|
||||||
|
has(key) {
|
||||||
|
return !!this.config[key]
|
||||||
|
}
|
||||||
|
|
||||||
|
del(key) {
|
||||||
|
delete this.config[key]
|
||||||
|
}
|
||||||
|
|
||||||
|
clear() {
|
||||||
|
this.config = this.createBaseConfig()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
// Returns true for `true`, true, positive numbers
|
||||||
|
// Returns false for `false`, false, 0, negative integers and anything else
|
||||||
|
function isTruthy(value) {
|
||||||
|
// Return if Boolean
|
||||||
|
if (typeof value === `boolean`) return value
|
||||||
|
|
||||||
|
// Return false if null or undefined
|
||||||
|
if (value === undefined || value === null) return false
|
||||||
|
|
||||||
|
// If the String is true or false
|
||||||
|
if (value.toLowerCase() === `true`) return true
|
||||||
|
if (value.toLowerCase() === `false`) return false
|
||||||
|
|
||||||
|
// Now check if it's a number
|
||||||
|
const number = parseInt(value, 10)
|
||||||
|
if (isNaN(number)) return false
|
||||||
|
if (number > 0) return true
|
||||||
|
|
||||||
|
// Default to false
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
export default isTruthy
|
||||||
@@ -0,0 +1,121 @@
|
|||||||
|
import path from "path"
|
||||||
|
import {
|
||||||
|
appendFileSync,
|
||||||
|
statSync,
|
||||||
|
readFileSync,
|
||||||
|
renameSync,
|
||||||
|
readdirSync,
|
||||||
|
existsSync,
|
||||||
|
unlinkSync,
|
||||||
|
} from "fs"
|
||||||
|
|
||||||
|
import isTruthy from "./is-truthy"
|
||||||
|
|
||||||
|
const MEDUSA_TELEMETRY_VERBOSE = process.env.MEDUSA_TELEMETRY_VERBOSE || false
|
||||||
|
|
||||||
|
class Outbox {
|
||||||
|
constructor(baseDir) {
|
||||||
|
this.eventsJsonFileName = `events.json`
|
||||||
|
this.bufferFilePath = path.join(baseDir, this.eventsJsonFileName)
|
||||||
|
this.baseDir = baseDir
|
||||||
|
}
|
||||||
|
|
||||||
|
appendToBuffer(event) {
|
||||||
|
try {
|
||||||
|
appendFileSync(this.bufferFilePath, event, `utf8`)
|
||||||
|
} catch (e) {
|
||||||
|
if (isTruthy(MEDUSA_TELEMETRY_VERBOSE)) {
|
||||||
|
console.error("Failed to append to buffer", e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getSize() {
|
||||||
|
if (!existsSync(this.bufferFilePath)) {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const stats = statSync(this.bufferFilePath)
|
||||||
|
return stats.size
|
||||||
|
} catch (e) {
|
||||||
|
if (isTruthy(MEDUSA_TELEMETRY_VERBOSE)) {
|
||||||
|
console.error("Failed to get outbox size", e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
getCount() {
|
||||||
|
if (!existsSync(this.bufferFilePath)) {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const fileBuffer = readFileSync(this.bufferFilePath)
|
||||||
|
const str = fileBuffer.toString()
|
||||||
|
const lines = str.split("\n")
|
||||||
|
return lines.length - 1
|
||||||
|
} catch (e) {
|
||||||
|
if (isTruthy(MEDUSA_TELEMETRY_VERBOSE)) {
|
||||||
|
console.error("Failed to get outbox count", e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
async flushFile(filePath, flushOperation) {
|
||||||
|
const now = `${Date.now()}-${process.pid}`
|
||||||
|
let success = false
|
||||||
|
let contents = ``
|
||||||
|
try {
|
||||||
|
if (!existsSync(filePath)) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
// Unique temporary file name across multiple concurrent Medusa instances
|
||||||
|
const newPath = `${this.bufferFilePath}-${now}`
|
||||||
|
renameSync(filePath, newPath)
|
||||||
|
contents = readFileSync(newPath, `utf8`)
|
||||||
|
unlinkSync(newPath)
|
||||||
|
|
||||||
|
// There is still a chance process dies while sending data and some events are lost
|
||||||
|
// This will be ok for now, however
|
||||||
|
success = await flushOperation(contents)
|
||||||
|
} catch (e) {
|
||||||
|
if (isTruthy(MEDUSA_TELEMETRY_VERBOSE)) {
|
||||||
|
console.error("Failed to perform file flush", e)
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
// if sending fails, we write the data back to the log
|
||||||
|
if (!success) {
|
||||||
|
if (isTruthy(MEDUSA_TELEMETRY_VERBOSE)) {
|
||||||
|
console.error(
|
||||||
|
"File flush did not succeed - writing back to file",
|
||||||
|
success
|
||||||
|
)
|
||||||
|
}
|
||||||
|
this.appendToBuffer(contents)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
async startFlushEvents(flushOperation) {
|
||||||
|
try {
|
||||||
|
await this.flushFile(this.bufferFilePath, flushOperation)
|
||||||
|
const files = readdirSync(this.baseDir)
|
||||||
|
const filtered = files.filter(p => p.startsWith(`events.json`))
|
||||||
|
for (const file of filtered) {
|
||||||
|
await this.flushFile(path.join(this.baseDir, file), flushOperation)
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
} catch (e) {
|
||||||
|
if (isTruthy(MEDUSA_TELEMETRY_VERBOSE)) {
|
||||||
|
console.error("Failed to perform flush", e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Outbox
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
import TelemetryDispatcher from "./telemetry-dispatcher"
|
||||||
|
|
||||||
|
const MEDUSA_TELEMETRY_HOST = process.env.MEDUSA_TELEMETRY_HOST || ""
|
||||||
|
const MEDUSA_TELEMETRY_PATH = process.env.MEDUSA_TELEMETRY_PATH || ""
|
||||||
|
|
||||||
|
const dispatcher = new TelemetryDispatcher({
|
||||||
|
host: MEDUSA_TELEMETRY_HOST,
|
||||||
|
path: MEDUSA_TELEMETRY_PATH,
|
||||||
|
})
|
||||||
|
dispatcher.dispatch()
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
import boxen from "boxen"
|
||||||
|
|
||||||
|
const defaultConfig = {
|
||||||
|
padding: 1,
|
||||||
|
borderColor: `blue`,
|
||||||
|
borderStyle: `double`,
|
||||||
|
}
|
||||||
|
|
||||||
|
const defaultMessage =
|
||||||
|
`Medusa collects anonymous usage analytics\n` +
|
||||||
|
`to help improve Medusa for all users.\n` +
|
||||||
|
`\n` +
|
||||||
|
`If you'd like to opt-out, you can use \`medusa telemetry --disable\`\n`
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Analytics notice for the end-user
|
||||||
|
*/
|
||||||
|
function showAnalyticsNotification(
|
||||||
|
config = defaultConfig,
|
||||||
|
message = defaultMessage
|
||||||
|
) {
|
||||||
|
console.log(boxen(message, config))
|
||||||
|
}
|
||||||
|
|
||||||
|
export default showAnalyticsNotification
|
||||||
@@ -0,0 +1,115 @@
|
|||||||
|
import removeSlash from "remove-trailing-slash"
|
||||||
|
import axios from "axios"
|
||||||
|
import axiosRetry from "axios-retry"
|
||||||
|
|
||||||
|
import showAnalyticsNotification from "./show-notification"
|
||||||
|
import Store from "../store"
|
||||||
|
import isTruthy from "./is-truthy"
|
||||||
|
|
||||||
|
const MEDUSA_TELEMETRY_VERBOSE = process.env.MEDUSA_TELEMETRY_VERBOSE || false
|
||||||
|
|
||||||
|
class TelemetryDispatcher {
|
||||||
|
constructor(options) {
|
||||||
|
this.store_ = new Store()
|
||||||
|
|
||||||
|
this.host = removeSlash(
|
||||||
|
options.host || "https://telemetry.medusa-commerce.com"
|
||||||
|
)
|
||||||
|
this.path = removeSlash(options.path || "/batch")
|
||||||
|
|
||||||
|
let axiosInstance = options.axiosInstance
|
||||||
|
if (!axiosInstance) {
|
||||||
|
axiosInstance = axios.create()
|
||||||
|
}
|
||||||
|
this.axiosInstance = axiosInstance
|
||||||
|
|
||||||
|
this.timeout = options.timeout || false
|
||||||
|
this.flushed = false
|
||||||
|
|
||||||
|
axiosRetry(this.axiosInstance, {
|
||||||
|
retries: 3,
|
||||||
|
retryDelay: axiosRetry.exponentialDelay,
|
||||||
|
retryCondition: this.isErrorRetryable_,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
isTrackingEnabled() {
|
||||||
|
// Cache the result
|
||||||
|
if (this.trackingEnabled !== undefined) {
|
||||||
|
return this.trackingEnabled
|
||||||
|
}
|
||||||
|
let enabled = this.store_.getConfig(`telemetry.enabled`)
|
||||||
|
if (enabled === undefined || enabled === null) {
|
||||||
|
showAnalyticsNotification()
|
||||||
|
enabled = true
|
||||||
|
this.store_.setConfig(`telemetry.enabled`, enabled)
|
||||||
|
}
|
||||||
|
this.trackingEnabled = enabled
|
||||||
|
return enabled
|
||||||
|
}
|
||||||
|
|
||||||
|
async dispatch() {
|
||||||
|
if (!this.isTrackingEnabled()) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.store_.flushEvents(async events => {
|
||||||
|
if (!events.length) {
|
||||||
|
if (isTruthy(MEDUSA_TELEMETRY_VERBOSE)) {
|
||||||
|
console.log("No events to POST - skipping")
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = {
|
||||||
|
batch: events,
|
||||||
|
timestamp: new Date(),
|
||||||
|
}
|
||||||
|
|
||||||
|
const req = {
|
||||||
|
headers: {},
|
||||||
|
}
|
||||||
|
|
||||||
|
return await this.axiosInstance
|
||||||
|
.post(`${this.host}${this.path}`, data, req)
|
||||||
|
.then(() => {
|
||||||
|
if (isTruthy(MEDUSA_TELEMETRY_VERBOSE)) {
|
||||||
|
console.log("POSTing batch succeeded")
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
.catch(e => {
|
||||||
|
if (isTruthy(MEDUSA_TELEMETRY_VERBOSE)) {
|
||||||
|
console.error("Failed to POST event batch", e)
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
isErrorRetryable_(error) {
|
||||||
|
// Retry Network Errors.
|
||||||
|
if (axiosRetry.isNetworkError(error)) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!error.response) {
|
||||||
|
// Cannot determine if the request can be retried
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Retry Server Errors (5xx).
|
||||||
|
if (error.response.status >= 500 && error.response.status <= 599) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Retry if rate limited.
|
||||||
|
if (error.response.status === 429) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default TelemetryDispatcher
|
||||||
File diff suppressed because it is too large.
Load diff
@@ -4,6 +4,7 @@ import express from "express"
|
|||||||
import { createConnection } from "typeorm"
|
import { createConnection } from "typeorm"
|
||||||
import { sync as existsSync } from "fs-exists-cached"
|
import { sync as existsSync } from "fs-exists-cached"
|
||||||
import { getConfigFile } from "medusa-core-utils"
|
import { getConfigFile } from "medusa-core-utils"
|
||||||
|
import { track } from "medusa-telemetry"
|
||||||
|
|
||||||
import Logger from "../loaders/logger"
|
import Logger from "../loaders/logger"
|
||||||
import loaders from "../loaders"
|
import loaders from "../loaders"
|
||||||
@@ -11,6 +12,7 @@ import loaders from "../loaders"
|
|||||||
import getMigrations from "./utils/get-migrations"
|
import getMigrations from "./utils/get-migrations"
|
||||||
|
|
||||||
const t = async function({ directory, migrate, seedFile }) {
|
const t = async function({ directory, migrate, seedFile }) {
|
||||||
|
track("CLI_SEED")
|
||||||
let resolvedPath = seedFile
|
let resolvedPath = seedFile
|
||||||
|
|
||||||
// If we are already given an absolute path we can skip resolution step
|
// If we are already given an absolute path we can skip resolution step
|
||||||
@@ -138,6 +140,8 @@ const t = async function({ directory, migrate, seedFile }) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
track("CLI_SEED_COMPLETED")
|
||||||
}
|
}
|
||||||
|
|
||||||
export default t
|
export default t
|
||||||
@@ -2,12 +2,15 @@ import "core-js/stable"
|
|||||||
import "regenerator-runtime/runtime"
|
import "regenerator-runtime/runtime"
|
||||||
|
|
||||||
import express from "express"
|
import express from "express"
|
||||||
|
import { track } from "medusa-telemetry"
|
||||||
|
|
||||||
import loaders from "../loaders"
|
import loaders from "../loaders"
|
||||||
import Logger from "../loaders/logger"
|
import Logger from "../loaders/logger"
|
||||||
|
|
||||||
export default async function({ port, directory }) {
|
export default async function({ port, directory }) {
|
||||||
async function start() {
|
async function start() {
|
||||||
|
track("CLI_START")
|
||||||
|
|
||||||
const app = express()
|
const app = express()
|
||||||
|
|
||||||
const { dbConnection } = await loaders({ directory, expressApp: app })
|
const { dbConnection } = await loaders({ directory, expressApp: app })
|
||||||
@@ -17,6 +20,7 @@ export default async function({ port, directory }) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
Logger.success(serverActivity, `Server is ready on port: ${port}`)
|
Logger.success(serverActivity, `Server is ready on port: ${port}`)
|
||||||
|
track("CLI_START_COMPLETED")
|
||||||
})
|
})
|
||||||
|
|
||||||
return { dbConnection, server }
|
return { dbConnection, server }
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import express from "express"
|
|||||||
import loaders from "../loaders"
|
import loaders from "../loaders"
|
||||||
|
|
||||||
export default async function({ directory, id, email, password, keepAlive }) {
|
export default async function({ directory, id, email, password, keepAlive }) {
|
||||||
|
track("CLI_USER", { with_id: !!id })
|
||||||
const app = express()
|
const app = express()
|
||||||
try {
|
try {
|
||||||
const { container } = await loaders({
|
const { container } = await loaders({
|
||||||
@@ -20,6 +21,8 @@ export default async function({ directory, id, email, password, keepAlive }) {
|
|||||||
process.exit(1)
|
process.exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
track("CLI_USER_COMPLETED", { with_id: !!id })
|
||||||
|
|
||||||
if (!keepAlive) {
|
if (!keepAlive) {
|
||||||
process.exit()
|
process.exit()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
import { createContainer, asValue } from "awilix"
|
import { createContainer, asValue } from "awilix"
|
||||||
import Redis from "ioredis"
|
import Redis from "ioredis"
|
||||||
import { getConfigFile } from "medusa-core-utils"
|
|
||||||
import requestIp from "request-ip"
|
import requestIp from "request-ip"
|
||||||
|
import { getManager } from "typeorm"
|
||||||
|
import { getConfigFile } from "medusa-core-utils"
|
||||||
|
import { track } from "medusa-telemetry"
|
||||||
|
|
||||||
import expressLoader from "./express"
|
import expressLoader from "./express"
|
||||||
import databaseLoader from "./database"
|
import databaseLoader from "./database"
|
||||||
@@ -14,7 +16,6 @@ import passportLoader from "./passport"
|
|||||||
import pluginsLoader, { registerPluginModels } from "./plugins"
|
import pluginsLoader, { registerPluginModels } from "./plugins"
|
||||||
import defaultsLoader from "./defaults"
|
import defaultsLoader from "./defaults"
|
||||||
import Logger from "./logger"
|
import Logger from "./logger"
|
||||||
import { getManager } from "typeorm"
|
|
||||||
|
|
||||||
export default async ({ directory: rootDirectory, expressApp }) => {
|
export default async ({ directory: rootDirectory, expressApp }) => {
|
||||||
const { configModule, configFilePath } = getConfigFile(
|
const { configModule, configFilePath } = getConfigFile(
|
||||||
@@ -62,53 +63,67 @@ export default async ({ directory: rootDirectory, expressApp }) => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
const modelsActivity = Logger.activity("Initializing models")
|
const modelsActivity = Logger.activity("Initializing models")
|
||||||
await modelsLoader({ container, activityId: modelsActivity })
|
track("MODELS_INIT_STARTED")
|
||||||
Logger.success(modelsActivity, "Models initialized")
|
modelsLoader({ container, activityId: modelsActivity })
|
||||||
|
const mAct = Logger.success(modelsActivity, "Models initialized") || {}
|
||||||
|
track("MODELS_INIT_COMPLETED", { duration: mAct.duration })
|
||||||
|
|
||||||
const pmActivity = Logger.activity("Initializing plugin models")
|
const pmActivity = Logger.activity("Initializing plugin models")
|
||||||
|
track("PLUGIN_MODELS_INIT_STARTED")
|
||||||
await registerPluginModels({
|
await registerPluginModels({
|
||||||
rootDirectory,
|
rootDirectory,
|
||||||
container,
|
container,
|
||||||
activityId: pmActivity,
|
activityId: pmActivity,
|
||||||
})
|
})
|
||||||
Logger.success(pmActivity, "Plugin models initialized")
|
const pmAct = Logger.success(pmActivity, "Plugin models initialized") || {}
|
||||||
|
track("PLUGIN_MODELS_INIT_COMPLETED", { duration: pmAct.duration })
|
||||||
|
|
||||||
const repoActivity = Logger.activity("Initializing repositories")
|
const repoActivity = Logger.activity("Initializing repositories")
|
||||||
await repositoriesLoader({ container, activityId: repoActivity })
|
track("REPOSITORIES_INIT_STARTED")
|
||||||
Logger.success(repoActivity, "Repositories initialized")
|
repositoriesLoader({ container, activityId: repoActivity }) || {}
|
||||||
|
const rAct = Logger.success(repoActivity, "Repositories initialized") || {}
|
||||||
|
track("REPOSITORIES_INIT_COMPLETED", { duration: rAct.duration })
|
||||||
|
|
||||||
const dbActivity = Logger.activity("Initializing database")
|
const dbActivity = Logger.activity("Initializing database")
|
||||||
|
track("DATABASE_INIT_STARTED")
|
||||||
const dbConnection = await databaseLoader({
|
const dbConnection = await databaseLoader({
|
||||||
container,
|
container,
|
||||||
configModule,
|
configModule,
|
||||||
activityId: dbActivity,
|
activityId: dbActivity,
|
||||||
})
|
})
|
||||||
Logger.success(dbActivity, "Database initialized")
|
const dbAct = Logger.success(dbActivity, "Database initialized") || {}
|
||||||
|
track("DATABASE_INIT_COMPLETED", { duration: dbAct.duration })
|
||||||
|
|
||||||
container.register({
|
container.register({
|
||||||
manager: asValue(dbConnection.manager),
|
manager: asValue(dbConnection.manager),
|
||||||
})
|
})
|
||||||
|
|
||||||
const servicesActivity = Logger.activity("Initializing services")
|
const servicesActivity = Logger.activity("Initializing services")
|
||||||
await servicesLoader({
|
track("SERVICES_INIT_STARTED")
|
||||||
|
servicesLoader({
|
||||||
container,
|
container,
|
||||||
configModule,
|
configModule,
|
||||||
activityId: servicesActivity,
|
activityId: servicesActivity,
|
||||||
})
|
})
|
||||||
Logger.success(servicesActivity, "Services initialized")
|
const servAct = Logger.success(servicesActivity, "Services initialized") || {}
|
||||||
|
track("SERVICES_INIT_COMPLETED", { duration: servAct.duration })
|
||||||
|
|
||||||
const subActivity = Logger.activity("Initializing subscribers")
|
const subActivity = Logger.activity("Initializing subscribers")
|
||||||
await subscribersLoader({ container, activityId: subActivity })
|
track("SUBSCRIBERS_INIT_STARTED")
|
||||||
Logger.success(subActivity, "Subscribers initialized")
|
subscribersLoader({ container, activityId: subActivity })
|
||||||
|
const subAct = Logger.success(subActivity, "Subscribers initialized") || {}
|
||||||
|
track("SUBSCRIBERS_INIT_COMPLETED", { duration: subAct.duration })
|
||||||
|
|
||||||
const expActivity = Logger.activity("Initializing express")
|
const expActivity = Logger.activity("Initializing express")
|
||||||
|
track("EXPRESS_INIT_STARTED")
|
||||||
await expressLoader({
|
await expressLoader({
|
||||||
app: expressApp,
|
app: expressApp,
|
||||||
configModule,
|
configModule,
|
||||||
activityId: expActivity,
|
activityId: expActivity,
|
||||||
})
|
})
|
||||||
await passportLoader({ app: expressApp, container, activityId: expActivity })
|
await passportLoader({ app: expressApp, container, activityId: expActivity })
|
||||||
Logger.success(expActivity, "Express intialized")
|
const exAct = Logger.success(expActivity, "Express intialized") || {}
|
||||||
|
track("EXPRESS_INIT_COMPLETED", { duration: exAct.duration })
|
||||||
|
|
||||||
// Add the registered services to the request scope
|
// Add the registered services to the request scope
|
||||||
expressApp.use((req, res, next) => {
|
expressApp.use((req, res, next) => {
|
||||||
@@ -120,26 +135,32 @@ export default async ({ directory: rootDirectory, expressApp }) => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
const pluginsActivity = Logger.activity("Initializing plugins")
|
const pluginsActivity = Logger.activity("Initializing plugins")
|
||||||
|
track("PLUGINS_INIT_STARTED")
|
||||||
await pluginsLoader({
|
await pluginsLoader({
|
||||||
container,
|
container,
|
||||||
rootDirectory,
|
rootDirectory,
|
||||||
app: expressApp,
|
app: expressApp,
|
||||||
activityId: pluginsActivity,
|
activityId: pluginsActivity,
|
||||||
})
|
})
|
||||||
Logger.success(pluginsActivity, "Plugins intialized")
|
const pAct = Logger.success(pluginsActivity, "Plugins intialized") || {}
|
||||||
|
track("PLUGINS_INIT_COMPLETED", { duration: pAct.duration })
|
||||||
|
|
||||||
const apiActivity = Logger.activity("Initializing API")
|
const apiActivity = Logger.activity("Initializing API")
|
||||||
|
track("API_INIT_STARTED")
|
||||||
await apiLoader({
|
await apiLoader({
|
||||||
container,
|
container,
|
||||||
rootDirectory,
|
rootDirectory,
|
||||||
app: expressApp,
|
app: expressApp,
|
||||||
activityId: apiActivity,
|
activityId: apiActivity,
|
||||||
})
|
})
|
||||||
Logger.success(apiActivity, "API initialized")
|
const apiAct = Logger.success(apiActivity, "API initialized") || {}
|
||||||
|
track("API_INIT_COMPLETED", { duration: apiAct.duration })
|
||||||
|
|
||||||
const defaultsActivity = Logger.activity("Initializing defaults")
|
const defaultsActivity = Logger.activity("Initializing defaults")
|
||||||
|
track("DEFAULTS_INIT_STARTED")
|
||||||
await defaultsLoader({ container, activityId: defaultsActivity })
|
await defaultsLoader({ container, activityId: defaultsActivity })
|
||||||
Logger.success(defaultsActivity, "Defaults initialized")
|
const dAct = Logger.success(defaultsActivity, "Defaults initialized") || {}
|
||||||
|
track("DEFAULTS_INIT_COMPLETED", { duration: dAct.duration })
|
||||||
|
|
||||||
return { container, dbConnection, app: expressApp }
|
return { container, dbConnection, app: expressApp }
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in new issue
Block a user