#!/usr/bin/env node const packageJson = require('./package.json'); const fetch = require('node-fetch'); const colors = require('colors'); const os = require('os'); const fs = require('fs'); const getopts = require('getopts'); const error = `[ERROR]: `.bold; console.log( `Grafana-Find (${packageJson.description})`.yellow.bold, `v${packageJson.version}`.cyan.bold ); const usage = { description: 'Utility to find strings in dashboards', params: { user: 'Grafana username', version: 'Display the version number and exit', help: 'Display this help message' } }; const opts = getopts(process.argv.slice(2), { alias: { version: 'v', help: 'h' }, boolean: [ 'version', 'help' ] }); if (opts.version) { process.exit(); } if (opts.help) { console.log(`Usage:`.gray, `grafana-find`, ``.magenta); process.exit(); } if (!opts._[0]) { console.error(`${error}The string to search for is missing`.red); process.exit(1); } if (opts._.length > 1) { console.error(`${error}This command doesn't allow more parameters`.red); process.exit(1); } let config; const configPaths = [ os.homedir(), `${__dirname}` ]; for (const configPath of configPaths) { const configFile = `${configPath}/.grafana-find.json`; if (fs.existsSync(configFile)) { config = require(configFile); break; } } if (!config) { console.error(`${error}Configuration file not found, search paths: .grafana-find.json: ${configPaths.join(':')}\n`.red); process.exit(1); } const findAll = opts._[0]; const grafanaUrl = config.grafanaUrl; let user = config.user; let passw = config.password; const grafanaApi = `${grafanaUrl}/api`; const urlOrganizations = `${grafanaUrl}/api/orgs`; const urlDashboards = `${grafanaApi}/search?orgId=`; const urlUID = `${grafanaApi}/dashboards/uid/`; let numberOfDashboards = 0; let totalDashboards = 0; let numberOfPanels = 0; let numberOfVariables = 0; let numberOfObjects = 0; let titlePanels = new Array; let nameVariables = new Array; const regexRawSQL = new RegExp(findAll, 'i'); async function main(){ if (!user) { const readline = require('readline'); const rl = readline.createInterface({ input: process.stdin, output: process.stdout }); const answer = await new Promise(resolve => { rl.question(colors.green('Enter your user: '), resolve); }); user = `${answer}`; if (!answer) { console.error(`\n${error}You need to put a user\n`.red); process.exit(0); } rl.close(); } if (!passw) { const readline = require('readline'); const rl = readline.createInterface({ input: process.stdin, output: process.stdout }); rl.stdoutMuted = true; const answer = await new Promise(resolve => { rl.question(colors.green('Enter your password: '), resolve); rl._writeToOutput = function _writeToOutput(stringToWrite) { if (rl.stdoutMuted) rl.output.write("*"); else rl.output.write(stringToWrite); }; }); passw = `${answer}`; if (!answer) { console.error(`\n${error}You need to put a password\n`.red); process.exit(0); } rl.close(); } const credentials = `Basic ` + Buffer.from(`${user}:${passw}`).toString('base64'); try { var response = await fetch(urlOrganizations, { method: "GET", headers: { Authorization: credentials } }); } catch (notfound) { console.error(`${error}Server '${config.grafanaUrl}' not found`.red); process.exit(1); }; let AllOrganizations = await response.json(); if (AllOrganizations.message==='invalid username or password') { console.error(`\n${error}Invalid username or password\n`.red); process.exit(1); } console.clear(); console.log( `───────── Grafana-Find (${packageJson.description}) v${packageJson.version} ────────`.yellow.bold ); console.log(colors.green.bold(`──────────────────── Starting process ────────────────────\n`)); for (let x in AllOrganizations) { console.log(colors.gray.bold(`-{Organization: ${AllOrganizations[x].name}}-\n`)); response = await fetch(`${urlDashboards}${AllOrganizations[x].id}`, { method: "GET", headers: { Authorization: credentials }, redirect: 'manual' }); if (response.status === 302) { response = await fetch(`${urlDashboards}${AllOrganizations[x].id}`, { method: 'GET', headers: { Accept: 'application/json' }, redirect: 'manual' }); } let allUID = await response.json(); for (let i in allUID) { let url = `${urlUID}${allUID[i].uid}`; response = await fetch(url, { method: "GET", headers: { Authorization: credentials, }, redirect: 'manual' }); if (response.status === 404) { response = await fetch(url, { method: 'GET', headers: { Accept: 'application/json' }, redirect: 'manual' }); } let data = await response.json(); let isFound = false; let isFoundSomething = false; const dashboard = data.dashboard; if (dashboard) { if (dashboard.panels) for (const panel of dashboard.panels) { if (panel.targets) for (const target of panel.targets) { isFound = regexRawSQL.test(target.rawSql); if (isFound) { if (panel.title) if (panel.title==' ') titlePanels.push(`(null)`.italic); else titlePanels.push(panel.title); else titlePanels.push(`(undefined)`.italic); numberOfPanels++; isFoundSomething=true; } } } if (dashboard.templating) for (const list of dashboard.templating.list) { isFound = regexRawSQL.test(list.query); if (isFound) { nameVariables.push(list.name) numberOfVariables++; isFoundSomething=true; } } } if (isFoundSomething) { const linkUrl = `${grafanaUrl}/d/${allUID[i].uid}?orgId=${AllOrganizations[x].id}`; console.log((linkUrl).yellow.underline, dashboard.title); if (numberOfPanels) { console.log(colors.cyan.bold(`[${numberOfPanels}] panels`)); for (let q in titlePanels) { if (q==(titlePanels.length-1)) { console.log(` └─${titlePanels[q]}`.cyan) break } console.log(` ├─${titlePanels[q]}`.cyan) } } if (numberOfVariables) { console.log(colors.magenta.bold(`[${numberOfVariables}] variables`)); for (let q in nameVariables) { if (q==(nameVariables.length-1)) { console.log(` └─${nameVariables[q]}`.magenta) break } console.log(` ├─${nameVariables[q]}`.magenta) } } console.log('') numberOfDashboards++; } titlePanels = []; nameVariables= []; numberOfObjects = numberOfPanels + numberOfVariables + numberOfObjects; numberOfPanels = 0; numberOfVariables = 0; } totalDashboards = numberOfDashboards + totalDashboards; if (!numberOfDashboards) console.log(`No results found\n`.green); numberOfDashboards = 0; }; console.log(colors.green.bold(`──────── Have been found ${numberOfObjects} results in ${totalDashboards} dashboards ────────\n`)); if (!response) { console.log(`${error}The server don't exists`); process.exit(1); } process.exit(); } main();