diff --git a/bin/cli b/bin/cli index 33560c53b..52d67c9af 100755 --- a/bin/cli +++ b/bin/cli @@ -9,13 +9,17 @@ const program = require('./commander'); program .command('serve', 'serve the application') + .command('settings', 'interact with the application settings') .command('assets', 'interact with assets') .command('setup', 'setup the application') .command('jobs', 'work with the job queues') .command('token', 'work with the access tokens') .command('users', 'work with the application auth') .command('migration', 'provides utilities for migrating the database') - .command('plugins', 'provides utilities for interacting with the plugin system') + .command( + 'plugins', + 'provides utilities for interacting with the plugin system' + ) .parse(process.argv); /** @@ -25,7 +29,10 @@ program * labled with the PID written out by the parent process. */ process.once('exit', () => { - if ((program.runningCommand.killed === false) && (program.runningCommand.exitCode === null)) { + if ( + program.runningCommand.killed === false && + program.runningCommand.exitCode === null + ) { program.runningCommand.kill('SIGINT'); } }); diff --git a/bin/cli-settings b/bin/cli-settings new file mode 100755 index 000000000..f8768046c --- /dev/null +++ b/bin/cli-settings @@ -0,0 +1,59 @@ +#!/usr/bin/env node + +const program = require('./commander'); +const inquirer = require('inquirer'); +const mongoose = require('../services/mongoose'); +const SettingsService = require('../services/settings'); +const util = require('./util'); + +// Register the shutdown criteria. +util.onshutdown([() => mongoose.disconnect()]); + +/** + * Change the organization name + */ +async function changeOrgName() { + try { + let settings = await SettingsService.retrieve(); + + let {organizationName} = await inquirer.prompt([ + { + name: 'organizationName', + message: 'Organization Name', + default: settings.organizationName + } + ]); + + if (settings.organizationName !== organizationName) { + settings.organizationName = organizationName; + + await SettingsService.update(settings); + + console.log('Settings were updated.'); + } else { + console.log('No update needed, no change was made.'); + } + } catch (err) { + console.error(err); + util.shutdown(1); + } + + util.shutdown(); +} + +//============================================================================== +// Setting up the program command line arguments. +//============================================================================== + +program + .command('change-org-name') + .description('change the organization name') + .action(changeOrgName); + +program.parse(process.argv); + +// If there is no command listed, output help. +if (!process.argv.slice(2).length) { + program.outputHelp(); + util.shutdown(); +} diff --git a/client/coral-embed-stream/src/actions/stream.js b/client/coral-embed-stream/src/actions/stream.js index 9b5d94a9b..75c665315 100644 --- a/client/coral-embed-stream/src/actions/stream.js +++ b/client/coral-embed-stream/src/actions/stream.js @@ -1,4 +1,4 @@ -import {pym} from 'coral-framework'; +import pym from 'coral-framework/services/pym'; import * as actions from '../constants/stream'; export const setActiveReplyBox = (id) => ({type: actions.SET_ACTIVE_REPLY_BOX, id}); diff --git a/client/coral-embed-stream/src/containers/Embed.js b/client/coral-embed-stream/src/containers/Embed.js index bf05caf29..da7ca48fa 100644 --- a/client/coral-embed-stream/src/containers/Embed.js +++ b/client/coral-embed-stream/src/containers/Embed.js @@ -7,7 +7,9 @@ import branch from 'recompose/branch'; import renderComponent from 'recompose/renderComponent'; import {Spinner} from 'coral-ui'; -import {authActions, assetActions, pym} from 'coral-framework'; +import * as authActions from 'coral-framework/actions/auth'; +import * as assetActions from 'coral-framework/actions/asset'; +import pym from 'coral-framework/services/pym'; import {getDefinitionName} from 'coral-framework/utils'; import {withQuery} from 'coral-framework/hocs'; import Embed from '../components/Embed'; diff --git a/client/coral-embed-stream/src/containers/Stream.js b/client/coral-embed-stream/src/containers/Stream.js index e52aa000f..6655d7a06 100644 --- a/client/coral-embed-stream/src/containers/Stream.js +++ b/client/coral-embed-stream/src/containers/Stream.js @@ -8,7 +8,8 @@ import { withAddTag, withRemoveTag, withIgnoreUser, withEditComment, } from 'coral-framework/graphql/mutations'; -import {notificationActions, authActions} from 'coral-framework'; +import * as authActions from 'coral-framework/actions/auth'; +import * as notificationActions from 'coral-framework/actions/notification'; import {editName} from 'coral-framework/actions/user'; import {setActiveReplyBox} from '../actions/stream'; import Stream from '../components/Stream'; diff --git a/client/coral-embed-stream/src/index.js b/client/coral-embed-stream/src/index.js index 16c510c29..f5be3922d 100644 --- a/client/coral-embed-stream/src/index.js +++ b/client/coral-embed-stream/src/index.js @@ -7,8 +7,8 @@ import './graphql'; import {addExternalConfig} from 'coral-embed-stream/src/actions/config'; import {getStore, injectReducers} from 'coral-framework/services/store'; import {getClient} from 'coral-framework/services/client'; +import pym from 'coral-framework/services/pym'; import AppRouter from './AppRouter'; -import {pym} from 'coral-framework'; import {loadPluginsTranslations, injectPluginsReducers} from 'coral-framework/helpers/plugins'; import reducers from './reducers'; diff --git a/client/coral-framework/actions/auth.js b/client/coral-framework/actions/auth.js index 53d0f1212..732a11ebe 100644 --- a/client/coral-framework/actions/auth.js +++ b/client/coral-framework/actions/auth.js @@ -3,7 +3,7 @@ import bowser from 'bowser'; import * as actions from '../constants/auth'; import * as Storage from '../helpers/storage'; import coralApi, {base} from '../helpers/request'; -import pym from '../services/PymConnection'; +import pym from '../services/pym'; import {resetWebsocket} from 'coral-framework/services/client'; import t from 'coral-framework/services/i18n'; diff --git a/client/coral-framework/actions/index.js b/client/coral-framework/actions/index.js deleted file mode 100644 index 65f7b87d3..000000000 --- a/client/coral-framework/actions/index.js +++ /dev/null @@ -1,9 +0,0 @@ -import * as authActions from './auth'; -import * as assetActions from './asset'; -import * as notificationActions from './notification'; - -export default { - authActions, - assetActions, - notificationActions, -}; diff --git a/client/coral-framework/actions/notification.js b/client/coral-framework/actions/notification.js index d77a3a16c..57a7950f5 100644 --- a/client/coral-framework/actions/notification.js +++ b/client/coral-framework/actions/notification.js @@ -1,4 +1,4 @@ -import pym from '../services/PymConnection'; +import pym from '../services/pym'; import * as actions from '../constants/notification'; export const addNotification = (notifType, text) => { diff --git a/client/coral-framework/components/ClickOutside.js b/client/coral-framework/components/ClickOutside.js index e13ef4472..5348a9af7 100644 --- a/client/coral-framework/components/ClickOutside.js +++ b/client/coral-framework/components/ClickOutside.js @@ -1,7 +1,7 @@ import {Component, cloneElement, Children} from 'react'; import PropTypes from 'prop-types'; import {findDOMNode} from 'react-dom'; -import {pym} from 'coral-framework'; +import pym from 'coral-framework/services/pym'; export default class ClickOutside extends Component { static propTypes = { diff --git a/client/coral-framework/index.js b/client/coral-framework/index.js deleted file mode 100644 index 5cd9d6174..000000000 --- a/client/coral-framework/index.js +++ /dev/null @@ -1,10 +0,0 @@ -import pym from './services/PymConnection'; -import actions from './actions'; - -// TODO (bc): Deprecate old actions. Spreading actions is now needed. - -export default { - pym, - actions, - ...actions -}; diff --git a/client/coral-framework/reducers/auth.js b/client/coral-framework/reducers/auth.js index 7ab12cf53..92d676abe 100644 --- a/client/coral-framework/reducers/auth.js +++ b/client/coral-framework/reducers/auth.js @@ -1,6 +1,6 @@ import {Map, fromJS} from 'immutable'; import * as actions from '../constants/auth'; -import {pym} from 'coral-framework'; +import pym from 'coral-framework/services/pym'; const initialState = Map({ isLoading: false, diff --git a/client/coral-framework/services/PymConnection.js b/client/coral-framework/services/pym.js similarity index 77% rename from client/coral-framework/services/PymConnection.js rename to client/coral-framework/services/pym.js index 6c492f2b8..bb5d90a0e 100644 --- a/client/coral-framework/services/PymConnection.js +++ b/client/coral-framework/services/pym.js @@ -1,4 +1,4 @@ -import Pym from '../../../node_modules/pym.js'; +import Pym from 'pym.js'; const pym = new Pym.Child({polling: 100}); export default pym; diff --git a/client/coral-settings/containers/ProfileContainer.js b/client/coral-settings/containers/ProfileContainer.js index 5e343e72f..a4cb9459f 100644 --- a/client/coral-settings/containers/ProfileContainer.js +++ b/client/coral-settings/containers/ProfileContainer.js @@ -5,7 +5,7 @@ import {bindActionCreators} from 'redux'; import {withStopIgnoringUser} from 'coral-framework/graphql/mutations'; -import {link} from 'coral-framework/services/PymConnection'; +import {link} from 'coral-framework/services/pym'; import NotLoggedIn from '../components/NotLoggedIn'; import IgnoredUsers from '../components/IgnoredUsers'; import {Spinner} from 'coral-ui'; diff --git a/config.js b/config.js index 02f95d070..ce8931235 100644 --- a/config.js +++ b/config.js @@ -29,7 +29,7 @@ const CONFIG = { // JWT_ISSUER is the value for the issuer for the tokens that will be verified // when decoding. If `JWT_ISSUER` is not in the environment, then it will try // `TALK_ROOT_URL`, otherwise, it will be undefined. - JWT_ISSUER: process.env.TALK_JWT_ISSUER || process.env.TALK_ROOT_URL || undefined, + JWT_ISSUER: process.env.TALK_JWT_ISSUER || process.env.TALK_ROOT_URL, // JWT_EXPIRY is the time for which a given token is valid for. JWT_EXPIRY: process.env.TALK_JWT_EXPIRY || '1 day', @@ -91,7 +91,9 @@ const CONFIG = { if (process.env.NODE_ENV === 'test' && !CONFIG.JWT_SECRET) { CONFIG.JWT_SECRET = 'keyboard cat'; } else if (!CONFIG.JWT_SECRET) { - throw new Error('TALK_JWT_SECRET must be provided in the environment to sign/verify tokens'); + throw new Error( + 'TALK_JWT_SECRET must be provided in the environment to sign/verify tokens' + ); } //------------------------------------------------------------------------------ @@ -108,7 +110,7 @@ if (process.env.NODE_ENV === 'test' && !CONFIG.MONGO_URL) { // Reset the redis url in the event it hasn't been overrided and we are in a // testing environment. if (process.env.NODE_ENV === 'test' && !CONFIG.REDIS_URL) { - CONFIG.REDIS_URL = 'redis://localhost'; + CONFIG.REDIS_URL = 'redis://localhost/1'; } //------------------------------------------------------------------------------ @@ -119,10 +121,15 @@ if (process.env.NODE_ENV === 'test' && !CONFIG.REDIS_URL) { * This is true when the recaptcha secret is provided and the Recaptcha feature * is to be enabled. */ -CONFIG.RECAPTCHA_ENABLED = CONFIG.RECAPTCHA_SECRET && CONFIG.RECAPTCHA_SECRET.length > 0 && - CONFIG.RECAPTCHA_PUBLIC && CONFIG.RECAPTCHA_PUBLIC.length > 0; +CONFIG.RECAPTCHA_ENABLED = + CONFIG.RECAPTCHA_SECRET && + CONFIG.RECAPTCHA_SECRET.length > 0 && + CONFIG.RECAPTCHA_PUBLIC && + CONFIG.RECAPTCHA_PUBLIC.length > 0; if (!CONFIG.RECAPTCHA_ENABLED) { - console.warn('Recaptcha is not enabled for login/signup abuse prevention, set TALK_RECAPTCHA_SECRET and TALK_RECAPTCHA_PUBLIC to enable Recaptcha.'); + console.warn( + 'Recaptcha is not enabled for login/signup abuse prevention, set TALK_RECAPTCHA_SECRET and TALK_RECAPTCHA_PUBLIC to enable Recaptcha.' + ); } //------------------------------------------------------------------------------ @@ -138,7 +145,13 @@ if (!CONFIG.RECAPTCHA_ENABLED) { ]; if (requiredProps.some((prop) => !CONFIG[prop])) { - console.warn(`${requiredProps.map((v) => `TALK_${v}`).join(', ')} should be defined in the environment if you would like to send password reset emails from Talk`); + console.warn( + `${requiredProps + .map((v) => `TALK_${v}`) + .join( + ', ' + )} should be defined in the environment if you would like to send password reset emails from Talk` + ); } } diff --git a/package.json b/package.json index 218381896..8573fb191 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "talk", - "version": "2.2.1", + "version": "2.3.0", "description": "A better commenting experience from Mozilla, The New York Times, and the Washington Post. https://coralproject.net", "main": "app.js", "scripts": { diff --git a/test/helpers/redis.js b/test/helpers/redis.js new file mode 100644 index 000000000..02ab81b51 --- /dev/null +++ b/test/helpers/redis.js @@ -0,0 +1,15 @@ +const {createClient} = require('../../services/redis'); + +// Create a redis client to use for clearing the database. +const client = createClient(); + +module.exports.clearDB = () => + new Promise((resolve, reject) => + client.flushdb((err) => { + if (err) { + return reject(err); + } + + return resolve(); + }) + ); diff --git a/test/server/redis.js b/test/server/redis.js new file mode 100644 index 000000000..529548918 --- /dev/null +++ b/test/server/redis.js @@ -0,0 +1,3 @@ +const redis = require('../helpers/redis'); + +beforeEach(() => redis.clearDB());