chore: clean up util notify/prompt lint findings
All checks were successful
build-and-push / lint (push) Successful in 4s
build-and-push / sonar (push) Successful in 12s
build-and-push / docker (push) Successful in 1m11s

This commit is contained in:
nocci 2025-12-30 14:36:41 +00:00
parent e4b1f60a66
commit 9e2bc89ff2

View file

@ -19,7 +19,9 @@ export const delay = ms => new Promise(resolve => setTimeout(resolve, ms));
export const datetimeUTC = (d = new Date()) => d.toISOString().replace('T', ' ').replace('Z', ''); export const datetimeUTC = (d = new Date()) => d.toISOString().replace('T', ' ').replace('Z', '');
// same as datetimeUTC() but for local timezone, e.g., UTC + 2h for the above in DE // same as datetimeUTC() but for local timezone, e.g., UTC + 2h for the above in DE
export const datetime = (d = new Date()) => datetimeUTC(new Date(d.getTime() - d.getTimezoneOffset() * 60000)); export const datetime = (d = new Date()) => datetimeUTC(new Date(d.getTime() - d.getTimezoneOffset() * 60000));
export const filenamify = s => s.replaceAll(':', '.').replace(/[^a-z0-9 _\-.]/gi, '_'); // alternative: https://www.npmjs.com/package/filenamify - On Unix-like systems, / is reserved. On Windows, <>:"/\|?* along with trailing periods are reserved. export const filenamify = s => s
.replaceAll(':', '.')
.replaceAll(/[^a-z0-9 _\-.]/gi, '_'); // alternative: https://www.npmjs.com/package/filenamify - On Unix-like systems, / is reserved. On Windows, <>:"/\|?* along with trailing periods are reserved.
export const handleSIGINT = (context = null) => process.on('SIGINT', async () => { // e.g. when killed by Ctrl-C export const handleSIGINT = (context = null) => process.on('SIGINT', async () => { // e.g. when killed by Ctrl-C
console.error('\nInterrupted by SIGINT. Exit!'); // Exception shows where the script was:\n'); // killed before catch in docker... console.error('\nInterrupted by SIGINT. Exit!'); // Exception shows where the script was:\n'); // killed before catch in docker...
@ -90,24 +92,28 @@ export const stealth = async context => {
// alternative inquirer is big (node_modules 29MB, enquirer 9.7MB, prompts 9.8MB, none 9.4MB) and slower // alternative inquirer is big (node_modules 29MB, enquirer 9.7MB, prompts 9.8MB, none 9.4MB) and slower
// open issue: prevents handleSIGINT() to work if prompt is cancelled with Ctrl-C instead of Escape: https://github.com/enquirer/enquirer/issues/372 // open issue: prevents handleSIGINT() to work if prompt is cancelled with Ctrl-C instead of Escape: https://github.com/enquirer/enquirer/issues/372
import Enquirer from 'enquirer'; const enquirer = new Enquirer(); import Enquirer from 'enquirer'; const enquirer = new Enquirer();
const timeoutPlugin = timeout => enquirer => { // cancel prompt after timeout ms const timeoutPlugin = defaultTimeout => enquirer => { // cancel prompt after timeout ms; can be disabled per prompt via options.timeout = 0
enquirer.on('prompt', prompt => { const onPrompt = prompt => {
const effectiveTimeout = prompt.options?.timeout ?? defaultTimeout;
if (!effectiveTimeout) return;
const t = setTimeout(() => { const t = setTimeout(() => {
prompt.hint = () => 'timeout'; prompt.hint = () => 'timeout';
prompt.cancel(); prompt.cancel();
}, timeout); }, effectiveTimeout);
prompt.on('submit', () => clearTimeout(t)); const clear = () => clearTimeout(t);
prompt.on('cancel', () => clearTimeout(t)); prompt.on('submit', clear);
}); prompt.on('cancel', clear);
};
enquirer.on('prompt', onPrompt);
}; };
enquirer.use(timeoutPlugin(cfg.login_timeout)); // TODO may not want to have this timeout for all prompts; better extend Prompt and add a timeout prompt option enquirer.use(timeoutPlugin(cfg.login_timeout));
// single prompt that just returns the non-empty value instead of an object // single prompt that just returns the non-empty value instead of an object
// @ts-ignore // @ts-ignore
export const prompt = o => enquirer.prompt({ name: 'name', type: 'input', message: 'Enter value', ...o }).then(r => r.name).catch(() => {}); export const prompt = o => enquirer.prompt({ name: 'name', type: 'input', message: 'Enter value', ...o }).then(r => r.name).catch(() => {});
export const confirm = o => prompt({ type: 'confirm', message: 'Continue?', ...o }); export const confirm = o => prompt({ type: 'confirm', message: 'Continue?', ...o });
// notifications via apprise CLI // notifications via apprise CLI
import { execFile } from 'child_process'; import { execFile } from 'node:child_process';
import { cfg } from './config.js'; import { cfg } from './config.js';
export const notify = html => new Promise(resolve => { export const notify = html => new Promise(resolve => {
@ -115,10 +121,9 @@ export const notify = html => new Promise(resolve => {
if (cfg.debug) console.debug('notify: NOTIFY is not set!'); if (cfg.debug) console.debug('notify: NOTIFY is not set!');
return resolve(); return resolve();
} }
// const cmd = `apprise '${cfg.notify}' ${title} -i html -b '${html}'`; // this had problems if e.g. ' was used in arg; could have `npm i shell-escape`, but instead using safer execFile which takes args as array instead of exec which spawned a shell to execute the command
const args = [cfg.notify, '-i', 'html', '-b', `'${html}'`]; const args = [cfg.notify, '-i', 'html', '-b', `'${html}'`];
if (cfg.notify_title) args.push(...['-t', cfg.notify_title]); if (cfg.notify_title) args.push('-t', cfg.notify_title);
if (cfg.debug) console.debug(`apprise ${args.map(a => `'${a}'`).join(' ')}`); // this also doesn't escape, but it's just for info if (cfg.debug) console.debug(`apprise ${args.join(' ')}`); // this also doesn't escape, but it's just for info
execFile('apprise', args, (error, stdout, stderr) => { execFile('apprise', args, (error, stdout, stderr) => {
if (error) { if (error) {
console.log(`error: ${error.message}`); console.log(`error: ${error.message}`);