diff --git a/.circleci/config.yml b/.circleci/config.yml index 848d496e7..5d22aa7df 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -41,9 +41,9 @@ integration_job: &integration_job - attach_workspace: at: ~/coralproject/talk - <<: *create_indexes - - run: - name: Setup the database with defaults - command: ./bin/cli setup --defaults + # - run: + # name: Setup the database with defaults + # command: ./bin/cli setup --defaults - run: name: Run the integration tests command: bash .circleci/e2e.sh diff --git a/bin/cli-setup b/bin/cli-setup index 11da478c2..d2db3b30d 100755 --- a/bin/cli-setup +++ b/bin/cli-setup @@ -45,11 +45,10 @@ const performSetup = async () => { } catch (err) { // If the error is `not init`, then we're good, otherwise, it's something // else. - if (err instanceof ErrSettingsNotInit) { + if (!err instanceof ErrSettingsNotInit) { + throw err; return; } - - throw err; } if (program.defaults) { @@ -138,7 +137,7 @@ const performSetup = async () => { console.log("\nWe'll ask you some questions about your first admin user.\n"); - let user = await inquirer.prompt([ + let { username, email } = await inquirer.prompt([ { type: 'input', name: 'username', @@ -161,39 +160,46 @@ const performSetup = async () => { return 'Email is required'; }, }, - { - name: 'password', - message: 'Password', - type: 'password', - filter: password => { - return UsersService.isValidPassword(password).catch(err => { - throw err.message; - }); - }, - }, - { - name: 'confirmPassword', - message: 'Confirm Password', - type: 'password', - filter: (confirmPassword, { password }) => { - if (password !== confirmPassword) { - return Promise.reject(new Error('Passwords do not match')); - } - - return UsersService.isValidPassword(confirmPassword).catch(err => { - throw err.message; - }); - }, - }, ]); + let password = ''; + while (!password) { + answers = await inquirer.prompt([ + { + name: 'password', + message: 'Password', + type: 'password', + filter: password => { + try { + UsersService.isValidPassword(password); + } catch (err) { + throw err.message; + } + + return password; + }, + }, + { + name: 'confirmPassword', + message: 'Confirm Password', + type: 'password', + }, + ]); + + if (answers.password !== answers.confirmPassword) { + console.error('Passwords do not match'); + } else { + password = answers.password; + } + } + const ctx = Context.forSystem(); let { user: newUser } = await SetupService.setup(ctx, { settings: settings.toObject(), user: { - email: user.email, - username: user.username, - password: user.password, + email, + username, + password, }, }); diff --git a/client/coral-embed/src/Stream.js b/client/coral-embed/src/Stream.js index 09843618f..efb44bc3b 100644 --- a/client/coral-embed/src/Stream.js +++ b/client/coral-embed/src/Stream.js @@ -79,7 +79,7 @@ export default class Stream { const renderOnIntersect = () => onIntersect(this.el, () => this.render()); if (!window.IntersectionObserver) { // Include a polyfill for the intersection observer. - import('intersection-observer') + import(/* webpackChunkName: "intersection-observer" */ 'intersection-observer') .then(() => { // Polyfill applied. renderOnIntersect(); diff --git a/config.js b/config.js index 99600508a..e208f79ec 100644 --- a/config.js +++ b/config.js @@ -76,6 +76,10 @@ const CONFIG = { // on the scraper when it makes requests. SCRAPER_HEADERS: process.env.TALK_SCRAPER_HEADERS || '{}', + // HTTP_X_REQUEST_ID is a string which represents the request header where we + // should source the request ID from, otherwise, a new one will be generated. + HTTP_X_REQUEST_ID: process.env.TALK_HTTP_X_REQUEST_ID || null, + //------------------------------------------------------------------------------ // JWT based configuration //------------------------------------------------------------------------------ diff --git a/middleware/trace.js b/middleware/trace.js index 6ac6f4b2c..c051ba90c 100644 --- a/middleware/trace.js +++ b/middleware/trace.js @@ -1,11 +1,21 @@ +const { HTTP_X_REQUEST_ID } = require('../config'); const uuid = require('uuid/v1'); // Trace middleware attaches a request id to each incoming request. -module.exports = (req, res, next) => { - req.id = uuid(); +module.exports = HTTP_X_REQUEST_ID + ? (req, res, next) => { + req.id = req.get(HTTP_X_REQUEST_ID) || uuid(); - // Add the context ID to the request as an HTTP header. - res.set('X-Talk-Trace-ID', req.id); + // Add the context ID to the request as an HTTP header. + res.set('X-Talk-Trace-ID', req.id); - next(); -}; + next(); + } + : (req, res, next) => { + req.id = uuid(); + + // Add the context ID to the request as an HTTP header. + res.set('X-Talk-Trace-ID', req.id); + + next(); + }; diff --git a/package.json b/package.json index 56eb07acd..e45faabb9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "talk", - "version": "4.6.8", + "version": "4.6.9", "description": "A better commenting experience from Mozilla, The New York Times, and the Washington Post. https://coralproject.net", "main": "app.js", "private": true, @@ -105,7 +105,7 @@ "eventemitter2": "^4.1.2", "exports-loader": "^0.6.4", "express": "4.16.0", - "express-static-gzip": "^0.3.1", + "express-static-gzip": "^1.1.1", "extract-text-webpack-plugin": "^3.0.2", "file-loader": "^0.11.2", "final-form": "^4.8.1", diff --git a/routes/index.js b/routes/index.js index ce248795c..913c0862d 100644 --- a/routes/index.js +++ b/routes/index.js @@ -42,6 +42,26 @@ if (!DISABLE_STATIC_SERVER) { res.redirect(301, newEmbed); }); + /** + * setHeaders adds new headers related to caching to the static files that are + * served. + * + * @param res the response that can be used to set headers on + * @param path the path on the filesystem where the files are being served from + */ + const setHeaders = (res, path) => { + if (path.endsWith('embed.js')) { + // The embed.js file itself should not be cached for a long duration of + // time, as it may change based on the deploy. + res.setHeader('Cache-Control', 'public, max-age=3600'); + } else { + // All static files besides the embed.js file contain hashes, we should + // ensure that any other file is cached for a long duration of time. This + // is cached for 1 week. + res.setHeader('Cache-Control', 'public, max-age=604800, immutable'); + } + }; + /** * Serve the directories under dist. */ @@ -58,10 +78,11 @@ if (!DISABLE_STATIC_SERVER) { fileExtension: 'zz', }, ], + setHeaders, }) ); } else { - router.use('/static', express.static(dist)); + router.use('/static', express.static(dist, { setHeaders })); } } diff --git a/yarn.lock b/yarn.lock index 66d21c752..7aa48abdb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4277,10 +4277,10 @@ exports-loader@^0.6.4: loader-utils "^1.0.2" source-map "0.5.x" -express-static-gzip@^0.3.1: - version "0.3.2" - resolved "https://registry.yarnpkg.com/express-static-gzip/-/express-static-gzip-0.3.2.tgz#89ede84547a5717de3146315f62dc996c071a88d" - integrity sha512-xFOW5Lxrh4xLey5i6gGWHOFznJayGCxazUau0kq7ElUh1t7q2B6IlvWv4d3UJwJej+aXEu9os/VpzPvRchdNiA== +express-static-gzip@^1.1.1: + version "1.1.3" + resolved "https://registry.yarnpkg.com/express-static-gzip/-/express-static-gzip-1.1.3.tgz#345ea02637d9d5865777d6fb57ccc0884abcda65" + integrity sha512-k8Q4Dx4PDpzEb8kth4uiPWrBeJWJYSgnWMzNdjQUOsEyXfYKbsyZDkU/uXYKcorRwOie5Vzp4RMEVrJLMfB6rA== dependencies: serve-static "^1.12.3"