UNPKG

dataunlocker

Version:

DataUnlocker's command line interface utilities

106 lines (103 loc) 4.17 kB
import { getEnv, getRelativeFileToCwd, isFileExists } from '../../lib/utils/index.js'; import { createHash } from 'crypto'; import { mkdir, readFile, writeFile } from 'fs/promises'; import { basename, dirname, resolve } from 'path'; export default async function patch(args) { if (!args[0]) { return warnAndExit(`Please provide a file name to patch.`); } const file = resolve(args[0]); const isFile = await isFileExists(file); if (!isFile) { return warnAndExit(`File not found: ${file}`); } if (!file.endsWith('.js')) { return warnAndExit(`Only .js files are supported. Provided file not supported: ${file}`); } const js = (await readFile(file)).toString(); const id = args.id || (await getEnv('DATAUNLOCKER_ID')); const _env = (await getEnv('DATAUNLOCKER_ENV'))?.toLowerCase() || ''; const env = _env === 'prod' || _env === 'production' ? '' : _env; if (!id || !/^[0-9a-f]{24}$/.test(id)) { return warnAndExit(`Please specify a valid DataUnlocker ID as DATAUNLOCKER_ID env var or --id CLI param. Copy it from the DataUnlocker dashboard.`); } if (args.backup && typeof args.backup !== 'string') { return warnAndExit(`Please specify the backup file name`); } if (args.backup && args['no-backup']) { return warnAndExit(`Conflicting options: --backup and --no-backup`); } const hash = createHash('sha256') .update(id) .update(basename(file)) .digest('hex') .slice(0, 7); const fileBackup = args.backup ? resolve(args.backup) : args['no-backup'] ? '' : `${file}.${hash}.backup`; const isBackupFileExists = fileBackup ? await isFileExists(fileBackup) : false; let jsToPatch = js; console.log(`Backup file: ${isBackupFileExists ? `exists (${getRelativeFileToCwd(fileBackup)})` : fileBackup ? 'does not exist' : 'disabled'}`); if (fileBackup && !isBackupFileExists) { console.info(` ↳ Backing up ${getRelativeFileToCwd(file)} -> ${getRelativeFileToCwd(fileBackup)}...`); await mkdir(dirname(fileBackup), { recursive: true }); await writeFile(fileBackup, jsToPatch); console.info(` ✔ Backed up to ${getRelativeFileToCwd(fileBackup)}`); } console.log(`Patching`); if (isBackupFileExists) { jsToPatch = (await readFile(fileBackup)).toString(); console.log(` ↳ Using backup file contents (${getRelativeFileToCwd(fileBackup)})`); } else { console.log(` ↳ Using original file contents (${getRelativeFileToCwd(file)})`); } if (args.endpoint) { console.log(` ↳ Using endpoint ${args.endpoint}`); } else { console.log(` ↳ Using the latest healthy endpoint (automatic)`); } console.log(` ↳ ${getRelativeFileToCwd(file)} will be overwritten`); console.log(` ↳ Patching, please wait...`); const url = `https://api${env ? `.${env}` : ''}.dataunlocker.com/domains/${id}/defender/patch-js${args.endpoint ? `?endpoint=${encodeURIComponent(args.endpoint)}` : ''}`; let result; try { result = await fetch(url, { method: 'POST', body: jsToPatch, headers: { 'Content-Type': 'application/x-www-form-urlencoded', }, }); } catch (e) { console.error(`POST ${url} failed, ${e}`); process.exit(1002); } let text = ''; try { text = await result.text(); } catch (e) { console.error(`POST ${url} failed, ${e}`); process.exit(1003); } if (result.status >= 300 || result.status < 200) { console.error(`POST ${url} failed, status ${result.status} ${result.statusText}\n\n${text}`); process.exit(1004); } console.log(` ↳ Writing ${getRelativeFileToCwd(file)}...`); await writeFile(file, text); console.log(` ✔ Done!`); } const warnAndExit = (message) => { console.warn(`❗️ ${message} Usage: $ npx dataunlocker patch file.js [--id 000000000000000000000000] [--no-backup] [--backup file.js.backup]`); process.exit(1); };