2023-12-20 21:08:07 +02:00
import BaseCommand from './base-command' ;
import { _ , setLocale } from '@joplin/lib/locale' ;
2017-11-03 02:09:34 +02:00
const { app } = require ( './app.js' ) ;
2023-12-20 21:08:07 +02:00
import * as fs from 'fs-extra' ;
import Setting , { AppType } from '@joplin/lib/models/Setting' ;
import { ReadStream } from 'tty' ;
2017-07-10 22:03:46 +02:00
class Command extends BaseCommand {
2023-12-20 21:08:07 +02:00
public override usage() {
2017-07-28 20:13:07 +02:00
return 'config [name] [value]' ;
2017-07-10 22:03:46 +02:00
}
2023-12-20 21:08:07 +02:00
public override description() {
2019-07-30 09:35:42 +02:00
return _ ( 'Gets or sets a config value. If [value] is not provided, it will show the value of [name]. If neither [name] nor [value] is provided, it will list the current configuration.' ) ;
2017-07-10 22:03:46 +02:00
}
2023-12-20 21:08:07 +02:00
public override options() {
2020-01-25 00:28:54 +02:00
return [
[ '-v, --verbose' , _ ( 'Also displays unset and hidden config variables.' ) ] ,
[ '--export' , 'Writes all settings to STDOUT as JSON including secure variables.' ] ,
[ '--import' , 'Reads in JSON formatted settings from STDIN.' ] ,
[ '--import-file <file>' , 'Reads in settings from <file>. <file> must contain valid JSON.' ] ,
] ;
2017-07-24 21:47:01 +02:00
}
2023-12-20 21:08:07 +02:00
private async __importSettings ( inputStream : ReadStream | fs . ReadStream ) {
return new Promise < void > ( ( resolve , reject ) = > {
2019-12-28 23:48:34 +02:00
// being defensive and not attempting to settle twice
let isSettled = false ;
2023-12-20 21:08:07 +02:00
const chunks : any = [ ] ;
2017-07-24 21:47:01 +02:00
2019-12-28 23:48:34 +02:00
inputStream . on ( 'readable' , ( ) = > {
let chunk ;
while ( ( chunk = inputStream . read ( ) ) !== null ) {
chunks . push ( chunk ) ;
}
} ) ;
inputStream . on ( 'end' , ( ) = > {
2020-03-14 01:46:14 +02:00
const json = chunks . join ( '' ) ;
2019-12-28 23:48:34 +02:00
let settingsObj ;
try {
settingsObj = JSON . parse ( json ) ;
2023-02-16 12:55:24 +02:00
} catch ( error ) {
2019-12-28 23:48:34 +02:00
isSettled = true ;
2023-02-16 12:55:24 +02:00
return reject ( new Error ( ` Invalid JSON passed to config --import: \ n ${ error . message } . ` ) ) ;
2019-12-28 23:48:34 +02:00
}
if ( settingsObj ) {
2023-06-30 10:39:21 +02:00
// eslint-disable-next-line github/array-foreach -- Old code before rule was applied
2019-12-28 23:48:34 +02:00
Object . entries ( settingsObj )
. forEach ( ( [ key , value ] ) = > {
Setting . setValue ( key , value ) ;
} ) ;
}
if ( ! isSettled ) {
isSettled = true ;
resolve ( ) ;
}
} ) ;
inputStream . on ( 'error' , ( error ) = > {
if ( ! isSettled ) {
isSettled = true ;
reject ( error ) ;
}
} ) ;
} ) ;
}
2023-12-20 21:08:07 +02:00
public override async action ( args : any ) {
2017-08-22 19:57:35 +02:00
const verbose = args . options . verbose ;
2019-12-28 23:48:34 +02:00
const isExport = args . options . export ;
const isImport = args . options . import || args . options . importFile ;
const importFile = args . options . importFile ;
2017-07-24 20:58:11 +02:00
2023-12-20 21:08:07 +02:00
const renderKeyValue = ( name : string ) = > {
2018-01-28 19:35:20 +02:00
const md = Setting . settingMetadata ( name ) ;
2018-01-09 22:25:31 +02:00
let value = Setting . value ( name ) ;
if ( typeof value === 'object' || Array . isArray ( value ) ) value = JSON . stringify ( value ) ;
2018-11-02 21:22:49 +02:00
if ( md . secure && value ) value = '********' ;
2018-01-28 19:35:20 +02:00
2017-07-24 20:58:11 +02:00
if ( Setting . isEnum ( name ) ) {
2017-08-22 19:57:35 +02:00
return _ ( '%s = %s (%s)' , name , value , Setting . enumOptionsDoc ( name ) ) ;
2017-07-24 20:58:11 +02:00
} else {
return _ ( '%s = %s' , name , value ) ;
}
2019-07-30 09:35:42 +02:00
} ;
2017-07-24 20:58:11 +02:00
2019-12-28 23:48:34 +02:00
if ( isExport || ( ! isImport && ! args . value ) ) {
2023-12-20 21:08:07 +02:00
const keys = Setting . keys ( ! verbose , AppType . Cli ) ;
2017-11-12 02:44:26 +02:00
keys . sort ( ) ;
2019-12-28 23:48:34 +02:00
if ( isExport ) {
2023-12-20 21:08:07 +02:00
const resultObj = keys . reduce < Record < string , any > > ( ( acc , key ) = > {
2019-12-28 23:48:34 +02:00
const value = Setting . value ( key ) ;
if ( ! verbose && ! value ) return acc ;
acc [ key ] = value ;
return acc ;
} , { } ) ;
// Printing the object in "pretty" format so it's easy to read/edit
this . stdout ( JSON . stringify ( resultObj , null , 2 ) ) ;
} else if ( ! args . name ) {
for ( let i = 0 ; i < keys . length ; i ++ ) {
const value = Setting . value ( keys [ i ] ) ;
if ( ! verbose && ! value ) continue ;
this . stdout ( renderKeyValue ( keys [ i ] ) ) ;
}
} else {
this . stdout ( renderKeyValue ( args . name ) ) ;
2017-07-10 22:03:46 +02:00
}
2019-12-28 23:48:34 +02:00
2020-01-25 00:28:54 +02:00
app ( ) . gui ( ) . showConsole ( ) ;
app ( ) . gui ( ) . maximizeConsole ( ) ;
2019-12-28 23:48:34 +02:00
2017-07-10 22:03:46 +02:00
return ;
}
2019-12-28 23:48:34 +02:00
if ( isImport ) {
2023-12-20 21:08:07 +02:00
let fileStream : ReadStream | fs . ReadStream = process . stdin ;
2019-12-28 23:48:34 +02:00
if ( importFile ) {
fileStream = fs . createReadStream ( importFile , { autoClose : true } ) ;
}
await this . __importSettings ( fileStream ) ;
} else {
Setting . setValue ( args . name , args . value ) ;
2017-07-10 22:03:46 +02:00
}
2017-07-18 20:49:47 +02:00
2022-07-23 09:31:32 +02:00
if ( args . name === 'locale' ) {
2017-07-18 20:49:47 +02:00
setLocale ( Setting . value ( 'locale' ) ) ;
}
2017-07-10 22:03:46 +02:00
await Setting . saveAll ( ) ;
}
}
2019-07-30 09:35:42 +02:00
module .exports = Command ;