#! /usr/bin/env node const program = require('commander') const path = require('path') const prompt = require('prompt-async') const readline = require('readline') const os = require('os') const fs = require('fs') const yaml = require('js-yaml') const config = require('./config') const { Seance, } = require ('./seance') // Set up Seance CLI notifications const seance = new Seance() seance.on('update', (e) => { console.log(e.message) }) seance.on('notification', (e) => { console.warn(e.message) }) seance.on('error', (e) => { console.error(e.message) }) program .version('1.0.0-dev') .description('pull posts from Medium and add them to a Ghost blog'); program.command('setup') .description('Initial setup and configuration') .action(async () => { console.log('Hello and welcome to Seance.') console.log( '\n\nWe\'re going to take you through some steps' + ' to set up your system.\n' ) var res prompt.start() console.log('First up: File uploads.') console.log( 'Would you like to upload your files via WebDAV, or just ' + 'copy them to a local folder on your filesystem? Type ' + '"webdav" or "local" to choose.\n' ) res = await prompt.get([ { name: 'upload_mode', default: 'webdav', pattern: /^(webdav|local)$/ig, message: 'Please enter "webdav" or "local"', }, ]) if (res.upload_mode == 'webdav') { console.log('You\'re going with WebDAV? Awesome!') console.log( 'Please enter your server url (including the port), ' + 'username, and password\n' ) res = await prompt.get([ { name: 'server_url', default: config.webdav.server_url || '' }, { name: 'username', default: config.webdav.username || '' }, { name: 'password', default: config.webdav.password || '' , hidden: true}, ]) config.webdav.server_url = res.server_url config.webdav.username = res.username config.webdav.password = res.password console.log(`\nOkay. So we have ${config.webdav.username} on ${config.webdav.server_url} with [ the password you set]`) console.log( '\nA couple more settings for your WebDAV: ' + 'we need to know the path prefix and the uploaded path prefix.\n' + 'The path prefix is the subfolder to which you upload, like ' + '`/seance-uploads`, while the uploaded path prefix is what '+ 'you\'d stick in front of the filename after uploading ' + '(like `https://media.mysite.com/seance-uploads`).\n' ) res = await prompt.get([ { name: 'path_prefix', default: config.webdav.path_prefix || '' }, { name: 'uploaded_path_prefix', default: config.uploaded_path_prefix || config.webdav.uploaded_path_prefix || '' }, ]) config.webdav.path_prefix = res.path_prefix config.uploaded_path_prefix = res.uploaded_path_prefix console.log(`Cool. So uploads to ${config.webdav.path_prefix} will be visible at ${config.uploaded_path_prefix}.`) } else if (res.upload_mode == 'local') { console.log('You\'re saving files locally? Smart!') console.log( 'Two settings we need to know to get things running ' + 'smoothly: we need the local path/folder where you\'ll be ' + 'uploading the files, and the uploaded path prefix.\n' + 'The local path is the folder to which you upload, like ' + '`/var/www/seance-uploads`, while the uploaded path prefix ' + 'is what you\'d stick in front of the filename after ' + 'uploading (like `https://media.mysite.com/seance-uploads`).\n' ) res = await prompt.get([ { name: 'path_prefix', default: config.local_upload.path_prefix || '' }, { name: 'uploaded_path_prefix', default: config.uploaded_path_prefix || '' }, ]) config.local_upload.path_prefix = res.path_prefix config.uploaded_path_prefix = res.uploaded_path_prefix console.log(`Cool. So uploads to ${config.local_upload.path_prefix} will be visible at ${config.uploaded_path_prefix}.`) } console.log('\n\nNext up: Ghost settings.') console.log( 'The Ghost URL is basically the link to your main ' + 'Ghost site, and the admin API key can be found by going to ' + 'your Settings -> Integrations page.\n' ) res = await prompt.get([ { name: 'url', default: config.ghost.url || '' }, { name: 'version', default: config.ghost.version || 'v2' }, { name: 'admin_key', default: config.ghost.admin_key || '' }, ]) config.ghost.url = res.url config.ghost.version = res.version config.ghost.admin_key = res.admin_key console.log(`Right. So that's Ghost ${config.ghost.version} running at ${config.ghost.url} with key ${config.ghost.admin_key}`) console.log('\n\nNow, a bit about the server.') console.log( 'This server usually runs on the main path (eg. example.com/).' + 'If you want to host seance on a different subpath (eg. ' + 'example.com/seance) then please enter that last part of the' + 'string now (eg. /seance).' ) res = await prompt.get([ { name: 'base_path', default: config.basePath || '/' }, ]) if (res.base_path == '/') { console.log('Okay. Serving on the root path then :)') } else { config.basePath = res.base_path console.log( 'Right, so we\'ll be serving at yoursite.com' + `${config.basePath} then :)` ) } console.log( '\n\nA final thing. Do you have a "scissors" or other image ' + 'used as a separator in your article? If so, enter the path ' + 'here and we\'ll check against it to auto-convert them to ' + 'Ghost-friendly separators. If you don\'t use scissors, you ' + 'can just press Enter without typing anything.\n' ) res = await prompt.get([ { name: 'scissors', default: config.scissors || '' }, ]) config.scissors = res.scissors if (config.scissors) { console.log(`Done. Scissors set to ${config.scissors}`) } else { console.log('No scissors? That\'s fine. You\'ll get there one day :)') } console.log('\n\nFinal confirmation: would you like to save these settings?') const { save } = await prompt.get(['save']) if (save == '' || (save[0] && save[0].toLowerCase() != 'n')) { const configDir = path.join(os.homedir(), '.config') if (!fs.existsSync(configDir)) { os.makeDirSync(configDir) } userConfig = path.join(configDir, 'seance.yaml') data = yaml.safeDump(config) fs.writeFileSync(userConfig, data) console.log('\nThat\'s it - we\'re done!') } else { console.log('Not saving? What a pity!') } }) program.command('fetch-medium ') .alias('fetch') .description('fetch a Medium post') .action((post_url) => { new Seance().fetchFromMedium(post_url) .then((post) => { console.info(`"${post.title}" fetched successfully.`) }) }); program.command('push-ghost ') .alias('push') .description('push a downloaded Medium post to Ghost') .option('-d, --dry-run', "full dry run: doesn't upload anything (same as --no-upload --no-push)") .option('--no-upload', "partial dry run: don't upload images") .option('--no-push', "partial dry run: don't push to ghost") .action((file, o) => { seance.pushToGhost(file, { dryRun: o.dryRun, noUpload: !o.upload, noPush: !o.push, }) }); program.command('medium-to-ghost ') .alias('import') .description('copy a Medium file over to Ghost') .action((mediumUrl) => { seance.pushToGhost(mediumUrl); }); program.command('create-user ') .description('create ghost-import.json to import Medium user to Ghost') .action(async (username, email) => { const jsonOut = await seance.generateUserData(username, email) .catch((err) => { console.log(`Error: ${err.error}`) return }) const userSlug = JSON.parse(jsonOut).data.users[0].slug const outFile = `user-${userSlug}.json` await fs.promises.writeFile(outFile, jsonOut, 'utf-8') console.log(`Saved to ${outFile}`); }); program.command('webdav-test ') .description('[test command] upload stuff to WebDAV') .action(async (file) => { // decide path current_date = new Date(); var dir_path = path.join( config.webdav.path_prefix, current_date.getUTCFullYear().toString(), current_date.getUTCMonth().toString(), 'test' // TODO: replace with article slug ) new seance.uploadDav(dir_path, file); }); program.command('check-scissors ') .description('[test command] check if an image matches the set separator') .action(async (file) => { console.log(await seance.checkScissors(file)) }) program.parse(process.argv)