Skip to content

Commit

Permalink
feat: user consent prompt and random seed instead of machine-id (#4)
Browse files Browse the repository at this point in the history
  • Loading branch information
pooya parsa committed May 29, 2020
1 parent 9f35a2f commit 4910922
Show file tree
Hide file tree
Showing 15 changed files with 1,049 additions and 618 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ node_modules
.DS_Store
coverage
dist
.nuxtrc
2 changes: 2 additions & 0 deletions bin/nuxt-telemetry.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#!/usr/bin/env node
require('../dist/cli.js').run()
6 changes: 1 addition & 5 deletions example/nuxt.config.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
import _module from '../src/module'

export default {
modules: [_module],
telemetry: {
debug: true
// url: 'http://localhost:8888'
}
modules: [_module]
}
22 changes: 15 additions & 7 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,17 +1,21 @@
{
"name": "@nuxt/telemetry",
"version": "0.0.4",
"description": "",
"repository": "nuxt/telemetry",
"license": "MIT",
"main": "dist/module.js",
"bin": {
"nuxt-telemetry": "./bin/nuxt-telemetry.js"
},
"files": [
"dist"
"dist",
"bin"
],
"scripts": {
"build": "yarn clean && bili src/module.ts",
"build": "yarn clean && bili src/module.ts,src/cli.ts",
"nuxt-telemetry": "ts-node src/cli.ts",
"clean": "rimraf dist",
"dev": "yarn example:dev",
"dev": "NUXT_TELEMETRY_DEBUG=true yarn example:dev",
"example:build": "nuxt-ts build example",
"example:dev": "nuxt-ts example",
"example:generate": "nuxt-ts generate example",
Expand All @@ -21,16 +25,19 @@
"test": "yarn lint"
},
"dependencies": {
"arg": "^4.1.3",
"ci-info": "^2.0.0",
"consola": "^2.11.3",
"defu": "^2.0.4",
"destr": "^0.1.4",
"fs-extra": "^9.0.0",
"git-url-parse": "^11.1.2",
"inquirer": "^7.1.0",
"is-docker": "^2.0.0",
"nanoid": "^3.1.9",
"node-fetch": "^2.6.0",
"node-machine-id": "^1.1.12",
"parse-git-config": "^3.0.0"
"parse-git-config": "^3.0.0",
"rc9": "^0.0.7"
},
"devDependencies": {
"@babel/preset-env": "latest",
Expand All @@ -39,11 +46,12 @@
"@nuxtjs/eslint-config-typescript": "latest",
"@nuxtjs/module-test-utils": "latest",
"@types/ci-info": "^2.0.0",
"@types/inquirer": "^6.5.0",
"babel-eslint": "latest",
"babel-jest": "latest",
"bili": "latest",
"eslint": "latest",
"nuxt-edge": "latest",
"nuxt-edge": "^2.13.0-26511579.a544fc72",
"rimraf": "latest",
"rollup-plugin-typescript2": "latest",
"standard-version": "latest",
Expand Down
65 changes: 65 additions & 0 deletions src/cli.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import { resolve } from 'path'
import { existsSync } from 'fs'
import arg from 'arg'
import * as rc from 'rc9'
import consola from 'consola'
import { usage, consentVersion } from './meta'

function _run () {
const args = arg({
'--global': Boolean,
'-g': '--global'
})

const [command, _dir] = args._
const dir = resolve(process.cwd(), _dir || '.')
const global = args['--global']

if (!global && !existsSync(resolve(dir, 'nuxt.config.js')) &&
!existsSync(resolve(dir, 'nuxt.config.ts'))) {
consola.error(`It seems you are not in a nuxt project (no nuxt.config found at ${dir})`)
showUsage()
}

switch (command) {
case 'enable':
setRC('telemetry.consent', consentVersion)
consola.success('Nuxt telemetry enabled for', global ? 'user' : dir)
consola.info('You can disable telemetry with `nuxt telemetry disable ' + (global ? '-g' : _dir))
return
case 'disable':
setRC('telemetry.consent', false)
consola.success('Nuxt telemetry disabled for', global ? 'user' : dir)
consola.info('You can enable telemetry with `nuxt telemetry enable ' + (global ? '-g' : _dir) + '`')
return
default:
showUsage()
}

function showUsage () {
consola.info(`Usage: ${usage}\n`)
process.exit(1)
}

function setRC (key, val) {
const update = { [key]: val }
if (global) {
rc.updateUser(update, '.nuxtrc')
} else {
rc.update(update, { name: '.nuxtrc', dir })
}
}
}

export function run () {
try {
_run()
} catch (err) {
consola.fatal(err)
process.exit(1)
}
}

if (!module.parent) {
run()
}
39 changes: 39 additions & 0 deletions src/consent.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import consola from 'consola'
import inquirer from 'inquirer'
import c from 'chalk'
import { updateUserNuxtRc } from './utils/nuxtrc'
import { TelemetryOptions } from './types'
import { usage, consentVersion } from './meta'

export async function ensureUserConsent (options: TelemetryOptions): Promise<boolean> {
if (options.consent >= consentVersion) {
return true
}

if (options.consent === false || !process.stdout.isTTY) {
return false
}

consola.info(`${c.green('NuxtJS')} collects completely anonymous data about usage.
This will help us improving developer experience over the time.
Read more: ${c.cyan.underline('https://git.io/nuxt-telemetry')}`)

const manualInstructions = `by setting ${c.cyan('telemetry: true|false')} in ${c.cyan('nuxt.config')} or\n Using ${c.cyan(usage)} or\n Setting ${c.cyan('NUXT_TELEMETRY_DISABLED')} environment variable`

const { accept } = await inquirer.prompt({
type: 'confirm',
name: 'accept',
message: 'Are you interested in participation?'
})
process.stdout.write('\n')

if (accept) {
consola.success(`Thanks for participating!\n You can always change your mind ${manualInstructions}`)
updateUserNuxtRc('telemetry.consent', consentVersion)
return true
}

consola.success(`Telemetry disabled for you machine.\n You can always change your mind ${manualInstructions}`)
updateUserNuxtRc('telemetry.consent', false)
return false
}
5 changes: 5 additions & 0 deletions src/meta.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export { name, version } from '../package.json'

export const usage = 'nuxt telemetry enable|disable [-g,--global] [dir]'

export const consentVersion = 1
29 changes: 27 additions & 2 deletions src/module.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,37 @@
import destr from 'destr'
import { Module } from '@nuxt/types'
import { nanoid } from 'nanoid'
import { name, version } from '../package.json'
import { updateUserNuxtRc } from './utils/nuxtrc'
import { Telemetry } from './telemetry'
import { getStats } from './utils/build-stats'
import { Stats, Nuxt, TelemetryOptions } from './types'
import { ensureUserConsent } from './consent'
import log from './utils/log'

export default <Module> function () {
async function telemetryModule () {
const options: TelemetryOptions = {
endpoint: destr(process.env.NUXT_TELEMETRY_ENDPOINT) || 'https://telemetry.nuxtjs.com',
debug: destr(process.env.NUXT_TELEMETRY_DEBUG),
...this.options.telemetry
}

if (!options.debug) {
log.level = 0
}

if (this.options.telemetry !== true && !await ensureUserConsent(options)) {
log.info('Telemetry disabled due to not user agreement!')
return
}

log.info('Telemetry enabled!')

if (!options.seed) {
options.seed = nanoid()
updateUserNuxtRc('telemetry.seed', options.seed)
log.info('Seed generated:', options.seed)
}

const t = new Telemetry(this.nuxt, options)

if (!this.options.dev) {
Expand Down Expand Up @@ -85,3 +106,7 @@ function profile (nuxt: Nuxt, t: Telemetry) {
})
}
}

telemetryModule.meta = { name, version }

export default telemetryModule
14 changes: 4 additions & 10 deletions src/telemetry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { EventsStorage } from './events-storage'
import * as events from './events/index'
import { createContext, getEventContext } from './utils/context'
import { Nuxt, Context, TelemetryOptions } from './types'
import logger from './utils/logger'
import log from './utils/log'

type FulfilledEvent = {
status: string
Expand Down Expand Up @@ -71,17 +71,11 @@ export class Telemetry {
if (this.options.endpoint) {
const start = Date.now()
try {
if (this.options.debug) {
logger.info('Sending events:', JSON.stringify(body, null, 2))
}
log.info('Sending events:', JSON.stringify(body, null, 2))
await postEvent(this.options.endpoint, body)
if (this.options.debug) {
logger.success(`Events sent to \`${this.options.endpoint}\` (${Date.now() - start} ms)`)
}
log.success(`Events sent to \`${this.options.endpoint}\` (${Date.now() - start} ms)`)
} catch (err) {
if (this.options.debug) {
logger.error(`Error sending sent to \`${this.options.endpoint}\` (${Date.now() - start} ms)\n`, err)
}
log.error(`Error sending sent to \`${this.options.endpoint}\` (${Date.now() - start} ms)\n`, err)
}
}
}
Expand Down
7 changes: 3 additions & 4 deletions src/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ export interface Event {
export interface TelemetryOptions {
debug: boolean,
endpoint: string,

seed: string,
consent?: number | boolean
}

export interface NuxtOptions {
Expand All @@ -21,9 +22,7 @@ export interface NuxtOptions {
_cli: boolean
ssr: boolean
dev: boolean
telemetry: {
url: string
}
telemetry: Partial<TelemetryOptions>,
buildModules: []
modules: []
}
Expand Down
21 changes: 7 additions & 14 deletions src/utils/context.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import os from 'os'
import gitUrlParse from 'git-url-parse'
import parseGitConfig from 'parse-git-config'
import { machineId } from 'node-machine-id'
import isDocker from 'is-docker'
import ci from 'ci-info'
import { Nuxt, Context, GitData } from '../types'
Expand All @@ -13,8 +12,8 @@ export async function createContext (nuxt: Nuxt): Promise<Context> {
const git = await getGit(rootDir)
const packageManager = await detectPackageManager(rootDir)

const sessionId = await getSessionId()
const projectId = await getProjectId(rootDir, git)
const sessionId = await nuxt.options.telemetry.seed
const projectId = await getProjectId(rootDir, git, sessionId)
const projectSession = getProjectSession(projectId, sessionId)

// @ts-ignore
Expand All @@ -25,9 +24,9 @@ export async function createContext (nuxt: Nuxt): Promise<Context> {
options: nuxt.options,
rootDir,
git,
sessionId, // machine ID
projectId, // git creds or path + machine ID
projectSession, // projectId + sessionId
sessionId, // hash(seed)
projectId, // hash(git upstream || path, seed)
projectSession, // hash(projectId, sessionId)
nuxtVersion,
isEdge: false, // TODO
isStart: false, // TODO
Expand Down Expand Up @@ -71,23 +70,17 @@ function getEnv (): Context['environment'] {
return 'unknown'
}

async function getSessionId () {
const id = await machineId()
return hash(id)
}

function getProjectSession (projectId: string, sessionId: string) {
return hash(`${projectId}#${sessionId}`)
}

async function getProjectId (rootDir: string, git?: GitData) {
function getProjectId (rootDir: string, git?: GitData, seed?: string) {
let id

if (git && git.url) {
id = `${git.source}#${git.owner}#${git.name}`
} else {
const entropy = await machineId()
id = `${rootDir}#${entropy}`
id = `${rootDir}#${seed}`
}

return hash(id)
Expand Down
File renamed without changes.
5 changes: 5 additions & 0 deletions src/utils/nuxtrc.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { updateUser } from 'rc9'

export function updateUserNuxtRc (key, val) {
updateUser({ [key]: val }, '.nuxtrc')
}
3 changes: 2 additions & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
"resolveJsonModule": true,
"moduleResolution": "Node",
"types": [
"node"
"node",
"inquirer"
]
},
"include": [
Expand Down
Loading

0 comments on commit 4910922

Please sign in to comment.