From d008947b4175d6eb018d20efdcdfc34366f0c572 Mon Sep 17 00:00:00 2001 From: Kim Gardner Date: Thu, 10 Aug 2017 15:51:50 +0100 Subject: [PATCH 1/8] Add important links to README --- README.md | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 89aaefee8..a58ac7c74 100644 --- a/README.md +++ b/README.md @@ -5,9 +5,21 @@ Online comments are broken. Our open-source Talk tool rethinks how moderation, c Third party licenses are available via the `/client/3rdpartylicenses.txt` endpoint when the server is running with built assets. -## Documentation +## Important Links -See our [Talk Documentation & Guides](https://coralproject.github.io/talk/). +- Developer Documentation & Setup Guides: https://coralproject.github.io/talk/ + +- Pivotal Tracker Backlog & Release Schedule: https://www.pivotaltracker.com/n/projects/1863625 + +## Learn More about Coral + +- Community Forums: https://community.coralproject.net/ + +- Website: https://coralproject.net + +- Blog: https://blog.coralproject.net + +- Community Guides for Journalism: https://guides.coralproject.net/ ## License From ada5e6f747c7740967f6488e11fbb6cf555479b4 Mon Sep 17 00:00:00 2001 From: Chi Vinh Le Date: Fri, 11 Aug 2017 19:53:08 +0700 Subject: [PATCH 2/8] Update apollo and fix warnings --- .../coral-embed-stream/src/graphql/index.js | 10 ++- package.json | 4 +- yarn.lock | 84 +++++++++++-------- 3 files changed, 57 insertions(+), 41 deletions(-) diff --git a/client/coral-embed-stream/src/graphql/index.js b/client/coral-embed-stream/src/graphql/index.js index f52cc4e7d..2c39235a2 100644 --- a/client/coral-embed-stream/src/graphql/index.js +++ b/client/coral-embed-stream/src/graphql/index.js @@ -152,8 +152,6 @@ const extension = { }, created_at: new Date().toISOString(), body, - parent_id, - asset_id, action_summaries: [], tags: tags.map((tag) => ({ tag: { @@ -169,8 +167,14 @@ const extension = { })), status: 'NONE', replyCount: 0, + asset: { + __typename: 'Asset', + id: asset_id, + title: '', + url: '', + }, parent: parent_id - ? {id: parent_id} + ? {__typename: 'Comment', id: parent_id} : null, replies: { __typename: 'CommentConnection', diff --git a/package.json b/package.json index bb0a724ac..ebe1138f6 100644 --- a/package.json +++ b/package.json @@ -116,7 +116,7 @@ "passport-local": "^1.0.0", "prop-types": "^15.5.10", "query-strings": "^0.0.1", - "react-apollo": "^1.1.0", + "react-apollo": "^1.4.12", "react-input-autosize": "^1.1.4", "react-recaptcha": "^2.2.6", "react-toastify": "^1.5.0", @@ -136,7 +136,7 @@ "yamljs": "^0.2.10" }, "devDependencies": { - "apollo-client": "^1.0.4", + "apollo-client": "^1.9.1", "autoprefixer": "^6.5.2", "babel-cli": "^6.24.0", "babel-core": "^6.24.0", diff --git a/yarn.lock b/yarn.lock index a5d73a756..9c1ac35e9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9,10 +9,6 @@ git-url-parse "^6.0.2" shelljs "^0.7.0" -"@types/async@^2.0.31": - version "2.0.40" - resolved "https://registry.yarnpkg.com/@types/async/-/async-2.0.40.tgz#ac02de68e66c004a61b7cb16df8b1db3a254cca9" - "@types/express-serve-static-core@*": version "4.0.44" resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.0.44.tgz#a1c3bd5d80e93c72fba91a03f5412c47f21d4ae7" @@ -26,18 +22,18 @@ "@types/express-serve-static-core" "*" "@types/serve-static" "*" +"@types/graphql@0.10.2": + version "0.10.2" + resolved "https://registry.yarnpkg.com/@types/graphql/-/graphql-0.10.2.tgz#d7c79acbaa17453b6681c80c34b38fcb10c4c08c" + "@types/graphql@^0.8.5", "@types/graphql@^0.8.6": version "0.8.6" resolved "https://registry.yarnpkg.com/@types/graphql/-/graphql-0.8.6.tgz#b34fb880493ba835b0c067024ee70130d6f9bb68" -"@types/graphql@^0.9.0", "@types/graphql@^0.9.1": +"@types/graphql@^0.9.1": version "0.9.1" resolved "https://registry.yarnpkg.com/@types/graphql/-/graphql-0.9.1.tgz#b04ebe84bc997cc60dbea2ed4d0d4342c737f99d" -"@types/isomorphic-fetch@0.0.33": - version "0.0.33" - resolved "https://registry.yarnpkg.com/@types/isomorphic-fetch/-/isomorphic-fetch-0.0.33.tgz#3ea1b86f8b73e6a7430d01d4dbd5b1f63fd72718" - "@types/mime@*": version "0.0.29" resolved "https://registry.yarnpkg.com/@types/mime/-/mime-0.0.29.tgz#fbcfd330573b912ef59eeee14602bface630754b" @@ -203,20 +199,27 @@ anymatch@^1.3.0: arrify "^1.0.0" micromatch "^2.1.5" -apollo-client@^1.0.2, apollo-client@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/apollo-client/-/apollo-client-1.0.4.tgz#af75db8cdd27e08a835ddfb39807849e178540f9" +apollo-client@^1.4.0, apollo-client@^1.9.1: + version "1.9.1" + resolved "https://registry.yarnpkg.com/apollo-client/-/apollo-client-1.9.1.tgz#9e6a383605572c755038cf5d7fdac9382bcdc040" dependencies: - graphql "^0.9.3" + apollo-link-core "^0.5.0" + graphql "^0.10.0" graphql-anywhere "^3.0.1" graphql-tag "^2.0.0" redux "^3.4.0" symbol-observable "^1.0.2" whatwg-fetch "^2.0.0" optionalDependencies: - "@types/async" "^2.0.31" - "@types/graphql" "^0.9.0" - "@types/isomorphic-fetch" "0.0.33" + "@types/graphql" "0.10.2" + +apollo-link-core@^0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/apollo-link-core/-/apollo-link-core-0.5.0.tgz#dc87da1aaa63b029321ae70938dc26257f5ab8c6" + dependencies: + graphql "^0.10.3" + graphql-tag "^2.4.2" + zen-observable-ts "^0.4.0" app-module-path@^2.2.0: version "2.2.0" @@ -3711,6 +3714,10 @@ graphql-tag@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/graphql-tag/-/graphql-tag-2.0.0.tgz#f3efe3b4d64f33bfe8479ae06a461c9d72f2a6fe" +graphql-tag@^2.4.2: + version "2.4.2" + resolved "https://registry.yarnpkg.com/graphql-tag/-/graphql-tag-2.4.2.tgz#6a63297d8522d03a2b72d26f1b239aab343840cd" + graphql-tools@^0.10.1: version "0.10.1" resolved "https://registry.yarnpkg.com/graphql-tools/-/graphql-tools-0.10.1.tgz#274aa338d50b1c0b3ed6936eafd8ed3a19ed1828" @@ -3721,13 +3728,19 @@ graphql-tools@^0.10.1: optionalDependencies: "@types/graphql" "^0.8.5" +graphql@^0.10.0, graphql@^0.10.3: + version "0.10.5" + resolved "https://registry.yarnpkg.com/graphql/-/graphql-0.10.5.tgz#c9be17ca2bdfdbd134077ffd9bbaa48b8becd298" + dependencies: + iterall "^1.1.0" + graphql@^0.7.2: version "0.7.2" resolved "https://registry.yarnpkg.com/graphql/-/graphql-0.7.2.tgz#cc894a32823399b8a0cb012b9e9ecad35cd00f72" dependencies: iterall "1.0.2" -graphql@^0.9.1, graphql@^0.9.3: +graphql@^0.9.1: version "0.9.3" resolved "https://registry.yarnpkg.com/graphql/-/graphql-0.9.3.tgz#71fc0fa331bffb9c20678485861cfb370803118e" dependencies: @@ -3876,6 +3889,10 @@ hoist-non-react-statics@^1.0.0, hoist-non-react-statics@^1.0.3, hoist-non-react- version "1.2.0" resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-1.2.0.tgz#aa448cf0986d55cc40773b17174b7dd066cb7cfb" +hoist-non-react-statics@^2.2.0: + version "2.2.2" + resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-2.2.2.tgz#c0eca5a7d5a28c5ada3107eb763b01da6bfa81fb" + home-or-tmp@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/home-or-tmp/-/home-or-tmp-2.0.0.tgz#e36c3f2d2cae7d746a857e38d18d5f32a7882db8" @@ -3996,11 +4013,11 @@ iconv-lite@0.4.13: version "0.4.13" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.13.tgz#1f88aba4ab0b1508e8312acc39345f36e992e2f2" -iconv-lite@0.4.15, iconv-lite@^0.4.5, iconv-lite@~0.4.13: +iconv-lite@0.4.15: version "0.4.15" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.15.tgz#fe265a218ac6a57cfe854927e9d04c19825eddeb" -iconv-lite@^0.4.17: +iconv-lite@^0.4.17, iconv-lite@^0.4.5, iconv-lite@~0.4.13: version "0.4.18" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.18.tgz#23d8656b16aae6742ac29732ea8f0336a4789cf2" @@ -4531,7 +4548,7 @@ iterall@1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/iterall/-/iterall-1.0.3.tgz#e0b31958f835013c323ff0b10943829ac69aa4b7" -iterall@^1.1.1: +iterall@^1.1.0, iterall@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/iterall/-/iterall-1.1.1.tgz#f7f0af11e9a04ec6426260f5019d9fcca4d50214" @@ -6912,14 +6929,14 @@ react-addons-test-utils@^15.4.2: fbjs "^0.8.4" object-assign "^4.1.0" -react-apollo@^1.1.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/react-apollo/-/react-apollo-1.1.1.tgz#136a6be6e0ed7bfa5292e67073e1829fe12e1e17" +react-apollo@^1.4.12: + version "1.4.12" + resolved "https://registry.yarnpkg.com/react-apollo/-/react-apollo-1.4.12.tgz#0cacbdd335acec4c1079feb48047df1c43f77bdf" dependencies: - apollo-client "^1.0.2" + apollo-client "^1.4.0" graphql-anywhere "^3.0.0" graphql-tag "^2.0.0" - hoist-non-react-statics "^1.2.0" + hoist-non-react-statics "^2.2.0" invariant "^2.2.1" lodash.flatten "^4.2.0" lodash.isequal "^4.1.1" @@ -6927,10 +6944,8 @@ react-apollo@^1.1.0: lodash.pick "^4.4.0" object-assign "^4.0.1" prop-types "^15.5.8" - optionalDependencies: - react-dom "0.14.x || 15.* || ^15.0.0" -"react-dom@0.14.x || 15.* || ^15.0.0", react-dom@^15.3.1, react-dom@^15.4.2: +react-dom@^15.3.1, react-dom@^15.4.2: version "15.5.4" resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-15.5.4.tgz#ba0c28786fd52ed7e4f2135fe0288d462aef93da" dependencies: @@ -7869,14 +7884,7 @@ string-width@^1.0.1, string-width@^1.0.2: is-fullwidth-code-point "^1.0.0" strip-ansi "^3.0.0" -string-width@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.0.0.tgz#635c5436cc72a6e0c387ceca278d4e2eec52687e" - dependencies: - is-fullwidth-code-point "^2.0.0" - strip-ansi "^3.0.0" - -string-width@^2.1.0: +string-width@^2.0.0, string-width@^2.1.0: version "2.1.1" resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" dependencies: @@ -8816,3 +8824,7 @@ yauzl@^2.5.0: dependencies: buffer-crc32 "~0.2.3" fd-slicer "~1.0.1" + +zen-observable-ts@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/zen-observable-ts/-/zen-observable-ts-0.4.0.tgz#a74bc9fe59747948a577bd513d438e70fcfae7e2" From 7360a43898878f3ce7314841c8daf7ee60c7c24f Mon Sep 17 00:00:00 2001 From: Chi Vinh Le Date: Fri, 11 Aug 2017 22:50:12 +0700 Subject: [PATCH 3/8] Remove immutable from embed stream --- .../containers/ConfigureStreamContainer.js | 2 +- .../src/containers/Embed.js | 2 +- .../src/containers/Stream.js | 5 +- .../coral-embed-stream/src/graphql/index.js | 6 +- client/coral-framework/actions/asset.js | 4 +- client/coral-framework/actions/auth.js | 27 +- client/coral-framework/actions/user.js | 21 -- client/coral-framework/constants/assets.js | 3 - client/coral-framework/constants/user.js | 10 - client/coral-framework/reducers/asset.js | 19 +- client/coral-framework/reducers/auth.js | 281 +++++++++++------- client/coral-framework/reducers/index.js | 2 - client/coral-framework/reducers/user.js | 43 --- .../containers/ProfileContainer.js | 11 +- plugin-api/beta/client/hocs/withReaction.js | 2 +- plugin-api/beta/client/hocs/withTags.js | 2 +- .../client/components/ChangeUsername.js | 2 +- .../client/components/SignInButton.js | 2 +- .../client/components/SignInContainer.js | 2 +- .../client/components/UserBox.js | 4 +- 20 files changed, 236 insertions(+), 214 deletions(-) delete mode 100644 client/coral-framework/actions/user.js delete mode 100644 client/coral-framework/constants/assets.js delete mode 100644 client/coral-framework/constants/user.js delete mode 100644 client/coral-framework/reducers/user.js diff --git a/client/coral-configure/containers/ConfigureStreamContainer.js b/client/coral-configure/containers/ConfigureStreamContainer.js index e7b937030..8489c809f 100644 --- a/client/coral-configure/containers/ConfigureStreamContainer.js +++ b/client/coral-configure/containers/ConfigureStreamContainer.js @@ -119,7 +119,7 @@ class ConfigureStreamContainer extends Component { } const mapStateToProps = (state) => ({ - asset: state.asset.toJS() + asset: state.asset }); const mapDispatchToProps = (dispatch) => ({ diff --git a/client/coral-embed-stream/src/containers/Embed.js b/client/coral-embed-stream/src/containers/Embed.js index 06646a6c2..e21a683d1 100644 --- a/client/coral-embed-stream/src/containers/Embed.js +++ b/client/coral-embed-stream/src/containers/Embed.js @@ -170,7 +170,7 @@ export const withEmbedQuery = withQuery(EMBED_QUERY, { }); const mapStateToProps = (state) => ({ - auth: state.auth.toJS(), + auth: state.auth, commentId: state.stream.commentId, assetId: state.stream.assetId, assetUrl: state.stream.assetUrl, diff --git a/client/coral-embed-stream/src/containers/Stream.js b/client/coral-embed-stream/src/containers/Stream.js index 73676713c..451b115a9 100644 --- a/client/coral-embed-stream/src/containers/Stream.js +++ b/client/coral-embed-stream/src/containers/Stream.js @@ -10,7 +10,6 @@ import { 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, setActiveTab, viewAllComments} from '../actions/stream'; import Stream from '../components/Stream'; import Comment from './Comment'; @@ -26,7 +25,7 @@ import { } from '../graphql/utils'; import omit from 'lodash/omit'; -const {showSignInDialog} = authActions; +const {showSignInDialog, editName} = authActions; const {addNotification} = notificationActions; class StreamContainer extends React.Component { @@ -298,7 +297,7 @@ const fragments = { }; const mapStateToProps = (state) => ({ - auth: state.auth.toJS(), + auth: state.auth, refetching: state.embed.refetching, commentCountCache: state.stream.commentCountCache, activeReplyBox: state.stream.activeReplyBox, diff --git a/client/coral-embed-stream/src/graphql/index.js b/client/coral-embed-stream/src/graphql/index.js index f52cc4e7d..4cd4a5d23 100644 --- a/client/coral-embed-stream/src/graphql/index.js +++ b/client/coral-embed-stream/src/graphql/index.js @@ -147,8 +147,8 @@ const extension = { __typename: 'Comment', user: { __typename: 'User', - id: auth.toJS().user.id, - username: auth.toJS().user.username + id: auth.user.id, + username: auth.user.username }, created_at: new Date().toISOString(), body, @@ -162,7 +162,7 @@ const extension = { __typename: 'Tag' }, assigned_by: { - id: auth.toJS().user.id, + id: auth.user.id, __typename: 'User' }, __typename: 'TagLink' diff --git a/client/coral-framework/actions/asset.js b/client/coral-framework/actions/asset.js index 24739ddde..aab3caf3a 100644 --- a/client/coral-framework/actions/asset.js +++ b/client/coral-framework/actions/asset.js @@ -13,7 +13,7 @@ const updateAssetSettingsSuccess = (settings) => ({type: actions.UPDATE_ASSET_SE const updateAssetSettingsFailure = (error) => ({type: actions.UPDATE_ASSET_SETTINGS_FAILURE, error}); export const updateConfiguration = (newConfig) => (dispatch, getState) => { - const assetId = getState().asset.toJS().id; + const assetId = getState().asset.id; dispatch(updateAssetSettingsRequest()); coralApi(`/assets/${assetId}/settings`, {method: 'PUT', body: newConfig}) .then(() => { @@ -27,7 +27,7 @@ export const updateConfiguration = (newConfig) => (dispatch, getState) => { }; export const updateOpenStream = (closedBody) => (dispatch, getState) => { - const assetId = getState().asset.toJS().id; + const assetId = getState().asset.id; dispatch(fetchAssetRequest()); coralApi(`/assets/${assetId}/status`, {method: 'PUT', body: closedBody}) .then(() => { diff --git a/client/coral-framework/actions/auth.js b/client/coral-framework/actions/auth.js index f4cf5217e..7c6d3bf89 100644 --- a/client/coral-framework/actions/auth.js +++ b/client/coral-framework/actions/auth.js @@ -4,6 +4,7 @@ import * as actions from '../constants/auth'; import * as Storage from '../helpers/storage'; import coralApi, {base} from '../helpers/request'; import pym from '../services/pym'; +import {addNotification} from '../actions/notification'; import {resetWebsocket} from 'coral-framework/services/client'; import t from 'coral-framework/services/i18n'; @@ -220,7 +221,7 @@ const signUpSuccess = (user) => ({type: actions.FETCH_SIGNUP_SUCCESS, user}); const signUpFailure = (error) => ({type: actions.FETCH_SIGNUP_FAILURE, error}); export const fetchSignUp = (formData) => (dispatch, getState) => { - const redirectUri = getState().auth.toJS().redirectUri; + const redirectUri = getState().auth.redirectUri; dispatch(signUpRequest()); coralApi('/users', { @@ -257,7 +258,7 @@ const forgotPasswordFailure = (error) => ({ export const fetchForgotPassword = (email) => (dispatch, getState) => { dispatch(forgotPasswordRequest(email)); - const redirectUri = getState().auth.toJS().redirectUri; + const redirectUri = getState().auth.redirectUri; coralApi('/account/password/reset', { method: 'POST', body: {email, loc: redirectUri} @@ -351,7 +352,7 @@ const verifyEmailFailure = () => ({ }); export const requestConfirmEmail = (email) => (dispatch, getState) => { - const redirectUri = getState().auth.toJS().redirectUri; + const redirectUri = getState().auth.redirectUri; dispatch(verifyEmailRequest()); return coralApi('/users/resend-verify', { method: 'POST', @@ -378,3 +379,23 @@ export const setRedirectUri = (uri) => ({ type: actions.SET_REDIRECT_URI, uri, }); + +//============================================================================== +// Edit Username +//============================================================================== + +const editUsernameFailure = (error) => ({type: actions.EDIT_USERNAME_FAILURE, error}); +const editUsernameSuccess = () => ({type: actions.EDIT_USERNAME_SUCCESS}); + +export const editName = (username) => (dispatch) => { + return coralApi('/account/username', {method: 'PUT', body: {username}}) + .then(() => { + dispatch(editUsernameSuccess()); + dispatch(addNotification('success', t('framework.success_name_update'))); + }) + .catch((error) => { + console.error(error); + const errorMessage = error.translation_key ? t(`error.${error.translation_key}`) : error.toString(); + dispatch(editUsernameFailure(errorMessage)); + }); +}; diff --git a/client/coral-framework/actions/user.js b/client/coral-framework/actions/user.js deleted file mode 100644 index a6ad45d4a..000000000 --- a/client/coral-framework/actions/user.js +++ /dev/null @@ -1,21 +0,0 @@ -import {addNotification} from '../actions/notification'; -import coralApi from '../helpers/request'; -import * as actions from '../constants/auth'; - -import t from 'coral-framework/services/i18n'; - -const editUsernameFailure = (error) => ({type: actions.EDIT_USERNAME_FAILURE, error}); -const editUsernameSuccess = () => ({type: actions.EDIT_USERNAME_SUCCESS}); - -export const editName = (username) => (dispatch) => { - return coralApi('/account/username', {method: 'PUT', body: {username}}) - .then(() => { - dispatch(editUsernameSuccess()); - dispatch(addNotification('success', t('framework.success_name_update'))); - }) - .catch((error) => { - console.error(error); - const errorMessage = error.translation_key ? t(`error.${error.translation_key}`) : error.toString(); - dispatch(editUsernameFailure(errorMessage)); - }); -}; diff --git a/client/coral-framework/constants/assets.js b/client/coral-framework/constants/assets.js deleted file mode 100644 index 3883ee835..000000000 --- a/client/coral-framework/constants/assets.js +++ /dev/null @@ -1,3 +0,0 @@ -export const MULTIPLE_ASSETS_REQUEST = 'MULTIPLE_ASSETS_REQUEST'; -export const MULTIPLE_ASSETS_SUCCESS = 'MULTIPLE_ASSETS_SUCCESS'; -export const MULTIPLE_ASSSETS_FAILURE = 'MULTIPLE_ASSSETS_FAILURE'; diff --git a/client/coral-framework/constants/user.js b/client/coral-framework/constants/user.js deleted file mode 100644 index 1557a42c9..000000000 --- a/client/coral-framework/constants/user.js +++ /dev/null @@ -1,10 +0,0 @@ -export const EDIT_NAME_REQUEST = 'EDIT_NAME_REQUEST'; -export const EDIT_NAME_SUCCESS = 'EDIT_NAME_SUCCESS'; -export const EDIT_NAME_FAILURE = 'EDIT_NAME_FAILURE'; -export const COMMENTS_BY_USER_REQUEST = 'COMMENTS_BY_USER_REQUEST'; -export const COMMENTS_BY_USER_SUCCESS = 'COMMENTS_BY_USER_SUCCESS'; -export const COMMENTS_BY_USER_FAILURE = 'COMMENTS_BY_USER_FAILURE'; -export const LOGOUT_SUCCESS = 'LOGOUT_SUCCESS'; -export const UPDATE_USERNAME = 'UPDATE_USERNAME'; -export const IGNORE_USER_SUCCESS = 'IGNORE_USER_SUCCESS'; -export const STOP_IGNORING_USER_SUCCESS = 'STOP_IGNORING_USER_SUCCESS'; diff --git a/client/coral-framework/reducers/asset.js b/client/coral-framework/reducers/asset.js index f9d0a55e3..b9837eddd 100644 --- a/client/coral-framework/reducers/asset.js +++ b/client/coral-framework/reducers/asset.js @@ -1,24 +1,27 @@ -import {Map} from 'immutable'; import * as actions from '../constants/asset'; -const initialState = Map({ +const initialState = { closedAt: null, settings: null, title: null, url: null, - features: Map({}), + features: {}, status: 'open', moderation: null -}); +}; export default function asset (state = initialState, action) { switch (action.type) { case actions.FETCH_ASSET_SUCCESS: - return state - .merge(action.asset); + return { + ...state, + ...action.asset, + }; case actions.UPDATE_ASSET_SETTINGS_SUCCESS: - return state - .setIn(['settings'], action.settings); + return { + ...state, + settings: action.settings, + }; default: return state; } diff --git a/client/coral-framework/reducers/auth.js b/client/coral-framework/reducers/auth.js index 5481b7a5e..1e18ddffa 100644 --- a/client/coral-framework/reducers/auth.js +++ b/client/coral-framework/reducers/auth.js @@ -1,8 +1,7 @@ -import {Map, fromJS} from 'immutable'; import * as actions from '../constants/auth'; import pym from 'coral-framework/services/pym'; -const initialState = Map({ +const initialState = { isLoading: false, loggedIn: false, user: null, @@ -21,27 +20,34 @@ const initialState = Map({ fromSignUp: false, requireEmailConfirmation: false, redirectUri: pym.parentUrl || location.href, -}); +}; const purge = (user) => { - const {settings, profiles, ...userData} = user; // eslint-disable-line - return fromJS(userData); + const {settings, ...userData} = user; // eslint-disable-line + return userData; }; export default function auth (state = initialState, action) { switch (action.type) { case actions.FOCUS_SIGNIN_DIALOG: - return state - .set('signInDialogFocus', true); + return { + ...state, + signInDialogFocus: true, + }; case actions.BLUR_SIGNIN_DIALOG: - return state - .set('signInDialogFocus', false); - case actions.SHOW_SIGNIN_DIALOG : - return state - .set('showSignInDialog', true) - .set('signInDialogFocus', true); + return { + ...state, + signInDialogFocus: false, + }; + case actions.SHOW_SIGNIN_DIALOG: + return { + ...state, + showSignInDialog: true, + signInDialogFocus: true, + }; case actions.HIDE_SIGNIN_DIALOG : - return state.merge(Map({ + return { + ...state, isLoading: false, showSignInDialog: false, signInDialogFocus: false, @@ -53,125 +59,198 @@ export default function auth (state = initialState, action) { emailVerificationSuccess: false, emailVerificationLoading: false, successSignUp: false - })); - case actions.SHOW_CREATEUSERNAME_DIALOG : - return state - .set('showCreateUsernameDialog', true); - case actions.HIDE_CREATEUSERNAME_DIALOG : - return state.merge(Map({ - showCreateUsernameDialog: false - })); - case actions.CREATE_USERNAME_SUCCESS : - return state.merge(Map({ + }; + case actions.SHOW_CREATEUSERNAME_DIALOG: + return { + ...state, + showCreateUsernameDialog: true, + }; + case actions.HIDE_CREATEUSERNAME_DIALOG: + return { + ...state, showCreateUsernameDialog: false, - error: '' - })); - case actions.CREATE_USERNAME_FAILURE : - return state - .set('error', action.error); - case actions.CHANGE_VIEW : - return state - .set('error', '') - .set('view', action.view); + }; + case actions.CREATE_USERNAME_SUCCESS: + return { + ...state, + showCreateUsernameDialog: false, + error: '', + }; + case actions.CREATE_USERNAME_FAILURE: + return { + ...state, + error: action.error, + }; + case actions.CHANGE_VIEW: + return { + ...state, + error: action.error, + view: action.view, + }; case actions.CLEAN_STATE: return initialState; case actions.FETCH_SIGNIN_REQUEST: - return state - .set('isLoading', true); + return { + ...state, + isLoading: true, + }; case actions.CHECK_LOGIN_FAILURE: - return state - .set('checkedInitialLogin', true) - .set('loggedIn', false) - .set('user', null); + return { + ...state, + checkedInitialLogin: true, + loggedIn: false, + user: null, + }; case actions.CHECK_LOGIN_SUCCESS: - return state - .set('checkedInitialLogin', true) - .set('loggedIn', true) - .set('user', purge(action.user)); + return { + ...state, + checkedInitialLogin: true, + loggedIn: true, + user: purge(action.user), + }; case actions.FETCH_SIGNIN_SUCCESS: - return state - .set('loggedIn', true) - .set('user', purge(action.user)); + return { + ...state, + loggedIn: true, + user: purge(action.user), + }; case actions.FETCH_SIGNIN_FAILURE: - return state - .set('isLoading', false) - .set('error', action.error) - .set('user', null); + return { + ...state, + isLoading: false, + error: action.error, + user: null, + }; case actions.FETCH_SIGNUP_FACEBOOK_REQUEST: - return state - .set('fromSignUp', true); + return { + ...state, + fromSignUp: true, + }; case actions.FETCH_SIGNIN_FACEBOOK_REQUEST: - return state - .set('fromSignUp', false); + return { + ...state, + fromSignUp: false, + }; case actions.FETCH_SIGNIN_FACEBOOK_SUCCESS: - return state - .set('user', purge(action.user)) - .set('loggedIn', true); + return { + ...state, + loggedIn: true, + user: purge(action.user), + }; case actions.FETCH_SIGNIN_FACEBOOK_FAILURE: - return state - .set('error', action.error) - .set('user', null); + return { + ...state, + error: action.error, + user: null, + }; case actions.FETCH_SIGNUP_REQUEST: - return state - .set('isLoading', true); + return { + ...state, + isLoading: true, + }; case actions.FETCH_SIGNUP_FAILURE: - return state - .set('error', action.error) - .set('isLoading', false); + return { + ...state, + error: action.error, + isLoading: false, + }; case actions.FETCH_SIGNUP_SUCCESS: - return state - .set('isLoading', false) - .set('successSignUp', true); + return { + ...state, + isLoading: false, + successSignUp: true, + }; case actions.LOGOUT: - return state - .set('user', null) - .set('isLoading', false) - .set('loggedIn', false); + return { + ...state, + user: null, + isLoading: false, + loggedIn: false, + }; case actions.INVALID_FORM: - return state - .set('error', action.error); + return { + ...state, + error: action.error, + }; case actions.VALID_FORM: - return state - .set('error', ''); + return { + ...state, + error: '', + }; case actions.FETCH_FORGOT_PASSWORD_SUCCESS: - return state - .set('passwordRequestFailure', null) - .set('passwordRequestSuccess', 'If you have a registered account, a password reset link was sent to that email'); + return { + ...state, + passwordRequestFailure: null, + passwordRequestSuccess: 'If you have a registered account, a password reset link was sent to that email', + }; case actions.FETCH_FORGOT_PASSWORD_FAILURE: - return state - .set('passwordRequestFailure', 'There was an error sending your password reset email. Please try again soon!') - .set('passwordRequestSuccess', null); + return { + ...state, + passwordRequestFailure: 'There was an error sending your password reset email. Please try again soon!', + passwordRequestSuccess: null, + }; case actions.UPDATE_USERNAME: - return state - .setIn(['user', 'username'], action.username); + return { + ...state, + user: { + ...state.user, + username: action.username, + } + }; case actions.VERIFY_EMAIL_FAILURE: - return state - .set('emailVerificationFailure', true) - .set('emailVerificationLoading', false); + return { + ...state, + emailVerificationFailure: true, + emailVerificationLoading: false, + }; case actions.VERIFY_EMAIL_REQUEST: - return state.set('emailVerificationLoading', true); + return { + ...state, + emailVerificationLoading: true, + }; case actions.VERIFY_EMAIL_SUCCESS: - return state - .set('emailVerificationSuccess', true) - .set('emailVerificationLoading', false); + return { + ...state, + emailVerificationSuccess: true, + emailVerificationLoading: false, + }; case actions.SET_REQUIRE_EMAIL_VERIFICATION: - return state - .set('requireEmailConfirmation', action.required); + return { + ...state, + requireEmailConfirmation: action.required, + }; case actions.SET_REDIRECT_URI: - return state - .set('redirectUri', action.uri); + return { + ...state, + redirectUri: action.uri, + }; case 'APOLLO_SUBSCRIPTION_RESULT': if (action.operationName === 'UserBanned' && state.getIn(['user', 'id']) === action.variables.user_id) { - return state - .mergeIn(['user'], action.result.data.userBanned); + return { + ...state, + user: { + ...state.user, + ...action.result.data.userBanned, + }, + }; } if (action.operationName === 'UserSuspended' && state.getIn(['user', 'id']) === action.variables.user_id) { - return state - .mergeIn(['user'], action.result.data.userSuspended); + return { + ...state, + user: { + ...state.user, + ...action.result.data.userSuspended, + }, + }; } if (action.operationName === 'UsernameRejected' && state.getIn(['user', 'id']) === action.variables.user_id) { - return state - .mergeIn(['user'], action.result.data.usernameRejected); + return { + ...state, + user: { + ...state.user, + ...action.result.data.usernameRejected, + }, + }; } return state; default : diff --git a/client/coral-framework/reducers/index.js b/client/coral-framework/reducers/index.js index f1ac580dc..6b9b730ca 100644 --- a/client/coral-framework/reducers/index.js +++ b/client/coral-framework/reducers/index.js @@ -1,11 +1,9 @@ import auth from './auth'; -import user from './user'; import asset from './asset'; import {reducer as commentBox} from '../../talk-plugin-commentbox'; export default { auth, - user, asset, commentBox, }; diff --git a/client/coral-framework/reducers/user.js b/client/coral-framework/reducers/user.js deleted file mode 100644 index bc40cf3ae..000000000 --- a/client/coral-framework/reducers/user.js +++ /dev/null @@ -1,43 +0,0 @@ -import {Map} from 'immutable'; -import * as authActions from '../constants/auth'; -import * as actions from '../constants/user'; -import * as assetActions from '../constants/assets'; - -const initialState = Map({ - username: '', - profiles: [], - settings: {}, - myComments: [], - myAssets: [], // the assets from which myComments (above) originated -}); - -const purge = (user) => { - const {_id, created_at, updated_at, __v, roles, ...userData} = user; // eslint-disable-line - return userData; -}; - -export default function user (state = initialState, action) { - switch (action.type) { - case authActions.CHECK_LOGIN_SUCCESS: - return state.merge(Map(purge(action.user))); - case authActions.CHECK_LOGIN_FAILURE: - return initialState; - case authActions.FETCH_SIGNIN_SUCCESS: - return state.merge(Map(purge(action.user))); - case authActions.FETCH_SIGNIN_FAILURE: - return initialState; - case authActions.FETCH_SIGNIN_FACEBOOK_SUCCESS: - return state.merge(Map(purge(action.user))); - case authActions.FETCH_SIGNIN_FACEBOOK_FAILURE: - return initialState; - case actions.SAVE_BIO_SUCCESS: - return state.set('settings', action.settings); - case actions.COMMENTS_BY_USER_SUCCESS: - return state.set('myComments', action.comments); - case assetActions.MULTIPLE_ASSETS_SUCCESS: - return state.set('myAssets', action.assets); - case actions.LOGOUT_SUCCESS: - return initialState; - } - return state; -} diff --git a/client/coral-settings/containers/ProfileContainer.js b/client/coral-settings/containers/ProfileContainer.js index 0090847f7..e33c0b6cd 100644 --- a/client/coral-settings/containers/ProfileContainer.js +++ b/client/coral-settings/containers/ProfileContainer.js @@ -51,7 +51,7 @@ class ProfileContainer extends Component { }; render() { - const {auth, asset, showSignInDialog, stopIgnoringUser} = this.props; + const {auth, auth: {user}, asset, showSignInDialog, stopIgnoringUser} = this.props; const {me} = this.props.root; const loading = [1, 2, 4].indexOf(this.props.data.networkStatus) >= 0; @@ -63,14 +63,14 @@ class ProfileContainer extends Component { return ; } - const localProfile = this.props.user.profiles.find( + const localProfile = user.profiles.find( (p) => p.provider === 'local' ); const emailAddress = localProfile && localProfile.id; return (
-

{this.props.user.username}

+

{user.username}

{emailAddress ?

{emailAddress}

: null} {me.ignoredUsers && me.ignoredUsers.length @@ -138,9 +138,8 @@ const withProfileQuery = withQuery( `); const mapStateToProps = (state) => ({ - user: state.user.toJS(), - asset: state.asset.toJS(), - auth: state.auth.toJS() + asset: state.asset, + auth: state.auth }); const mapDispatchToProps = (dispatch) => diff --git a/plugin-api/beta/client/hocs/withReaction.js b/plugin-api/beta/client/hocs/withReaction.js index 7f43cc5b2..22c30d639 100644 --- a/plugin-api/beta/client/hocs/withReaction.js +++ b/plugin-api/beta/client/hocs/withReaction.js @@ -363,7 +363,7 @@ export default (reaction) => (WrappedComponent) => { ); const mapStateToProps = (state) => ({ - user: state.auth.toJS().user, + user: state.auth.user, }); const mapDispatchToProps = (dispatch) => diff --git a/plugin-api/beta/client/hocs/withTags.js b/plugin-api/beta/client/hocs/withTags.js index e9b5bdff0..b0a74f4d7 100644 --- a/plugin-api/beta/client/hocs/withTags.js +++ b/plugin-api/beta/client/hocs/withTags.js @@ -84,7 +84,7 @@ export default (tag) => (WrappedComponent) => { } const mapStateToProps = (state) => ({ - user: state.auth.toJS().user, + user: state.auth.user, }); const mapDispatchToProps = (dispatch) => diff --git a/plugins/talk-plugin-auth/client/components/ChangeUsername.js b/plugins/talk-plugin-auth/client/components/ChangeUsername.js index ed3c5ace0..c1ad081b1 100644 --- a/plugins/talk-plugin-auth/client/components/ChangeUsername.js +++ b/plugins/talk-plugin-auth/client/components/ChangeUsername.js @@ -122,7 +122,7 @@ class ChangeUsernameContainer extends React.Component { } const mapStateToProps = ({auth}) => ({ - auth: auth.toJS() + auth: auth }); const mapDispatchToProps = (dispatch) => diff --git a/plugins/talk-plugin-auth/client/components/SignInButton.js b/plugins/talk-plugin-auth/client/components/SignInButton.js index 0adb300a7..9c8a04b11 100644 --- a/plugins/talk-plugin-auth/client/components/SignInButton.js +++ b/plugins/talk-plugin-auth/client/components/SignInButton.js @@ -16,7 +16,7 @@ const SignInButton = ({loggedIn, showSignInDialog}) => ( ); const mapStateToProps = ({auth}) => ({ - loggedIn: auth.toJS().loggedIn + loggedIn: auth.loggedIn }); const mapDispatchToProps = (dispatch) => diff --git a/plugins/talk-plugin-auth/client/components/SignInContainer.js b/plugins/talk-plugin-auth/client/components/SignInContainer.js index 6950f4124..188651186 100644 --- a/plugins/talk-plugin-auth/client/components/SignInContainer.js +++ b/plugins/talk-plugin-auth/client/components/SignInContainer.js @@ -176,7 +176,7 @@ class SignInContainer extends React.Component { } const mapStateToProps = (state) => ({ - auth: state.auth.toJS() + auth: state.auth }); const mapDispatchToProps = (dispatch) => diff --git a/plugins/talk-plugin-auth/client/components/UserBox.js b/plugins/talk-plugin-auth/client/components/UserBox.js index 5b79b664a..61540d3c5 100644 --- a/plugins/talk-plugin-auth/client/components/UserBox.js +++ b/plugins/talk-plugin-auth/client/components/UserBox.js @@ -22,8 +22,8 @@ const UserBox = ({loggedIn, user, logout, onShowProfile}) => ( ); const mapStateToProps = ({auth}) => ({ - loggedIn: auth.toJS().loggedIn, - user: auth.toJS().user + loggedIn: auth.loggedIn, + user: auth.user }); const mapDispatchToProps = (dispatch) => From 2e8863285dc9f108222d2df66c1255246bb48712 Mon Sep 17 00:00:00 2001 From: Chi Vinh Le Date: Mon, 14 Aug 2017 16:49:00 +0700 Subject: [PATCH 4/8] Rm immutable-js from admin --- client/coral-admin/src/actions/assets.js | 2 +- client/coral-admin/src/actions/install.js | 6 +- client/coral-admin/src/actions/settings.js | 2 +- client/coral-admin/src/containers/Layout.js | 6 +- .../coral-admin/src/containers/UserDetail.js | 4 +- client/coral-admin/src/reducers/assets.js | 51 ++++--- client/coral-admin/src/reducers/auth.js | 60 +++++--- client/coral-admin/src/reducers/community.js | 101 ++++++++------ client/coral-admin/src/reducers/config.js | 13 +- client/coral-admin/src/reducers/install.js | 131 +++++++++++------- client/coral-admin/src/reducers/moderation.js | 45 ++++-- client/coral-admin/src/reducers/settings.js | 81 ++++++----- .../routes/Community/containers/Community.js | 4 +- .../src/routes/Community/containers/Table.js | 2 +- .../routes/Configure/containers/Configure.js | 4 +- .../routes/Dashboard/containers/Dashboard.js | 4 +- .../src/routes/Install/containers/Install.js | 2 +- .../Moderation/containers/Moderation.js | 6 +- .../src/routes/Stories/containers/Stories.js | 2 +- .../client/containers/ModSubscription.js | 2 +- 20 files changed, 316 insertions(+), 212 deletions(-) diff --git a/client/coral-admin/src/actions/assets.js b/client/coral-admin/src/actions/assets.js index 75bd2477d..b9ebe0ada 100644 --- a/client/coral-admin/src/actions/assets.js +++ b/client/coral-admin/src/actions/assets.js @@ -35,7 +35,7 @@ export const fetchAssets = (skip = '', limit = '', search = '', sort = '', filte // Update an asset state // Get comments to fill each of the three lists on the mod queue export const updateAssetState = (id, closedAt) => (dispatch) => { - dispatch({type: UPDATE_ASSET_STATE_REQUEST}); + dispatch({type: UPDATE_ASSET_STATE_REQUEST, id, closedAt}); return coralApi(`/assets/${id}/status`, {method: 'PUT', body: {closedAt}}) .then(() => dispatch({type: UPDATE_ASSET_STATE_SUCCESS})) .catch((error) => { diff --git a/client/coral-admin/src/actions/install.js b/client/coral-admin/src/actions/install.js index 33cefab63..6393c58dd 100644 --- a/client/coral-admin/src/actions/install.js +++ b/client/coral-admin/src/actions/install.js @@ -93,21 +93,21 @@ const validation = (formData, dispatch, next) => { }; export const submitSettings = () => (dispatch, getState) => { - const settingsFormData = getState().install.toJS().data.settings; + const settingsFormData = getState().install.data.settings; validation(settingsFormData, dispatch, function() { dispatch(nextStep()); }); }; export const submitUser = () => (dispatch, getState) => { - const userFormData = getState().install.toJS().data.user; + const userFormData = getState().install.data.user; validation(userFormData, dispatch, function() { dispatch(nextStep()); }); }; export const finishInstall = () => (dispatch, getState) => { - const data = getState().install.toJS().data; + const data = getState().install.data; dispatch(installRequest()); return coralApi('/setup', {method: 'POST', body: data}) .then(() => { diff --git a/client/coral-admin/src/actions/settings.js b/client/coral-admin/src/actions/settings.js index ba8b917ea..ca9062b9e 100644 --- a/client/coral-admin/src/actions/settings.js +++ b/client/coral-admin/src/actions/settings.js @@ -42,7 +42,7 @@ export const updateDomainlist = (listName, list) => { }; export const saveSettingsToServer = () => (dispatch, getState) => { - let settings = getState().settings.toJS(); + let settings = getState().settings; if (settings.charCount) { settings.charCount = parseInt(settings.charCount); } diff --git a/client/coral-admin/src/containers/Layout.js b/client/coral-admin/src/containers/Layout.js index 8b119fb72..ae022f2ef 100644 --- a/client/coral-admin/src/containers/Layout.js +++ b/client/coral-admin/src/containers/Layout.js @@ -74,10 +74,8 @@ class LayoutContainer extends Component { } const mapStateToProps = (state) => ({ - auth: state.auth.toJS(), - TALK_RECAPTCHA_PUBLIC: state.config - .get('data') - .get('TALK_RECAPTCHA_PUBLIC', null) + auth: state.auth, + TALK_RECAPTCHA_PUBLIC: state.config.data.TALK_RECAPTCHA_PUBLIC, }); const mapDispatchToProps = (dispatch) => ({ diff --git a/client/coral-admin/src/containers/UserDetail.js b/client/coral-admin/src/containers/UserDetail.js index e59629a85..2ae6e4c9c 100644 --- a/client/coral-admin/src/containers/UserDetail.js +++ b/client/coral-admin/src/containers/UserDetail.js @@ -169,8 +169,8 @@ const mapStateToProps = (state) => ({ selectedCommentIds: state.userDetail.selectedCommentIds, statuses: state.userDetail.statuses, activeTab: state.userDetail.activeTab, - bannedWords: state.settings.toJS().wordlist.banned, - suspectWords: state.settings.toJS().wordlist.suspect, + bannedWords: state.settings.wordlist.banned, + suspectWords: state.settings.wordlist.suspect, }); const mapDispatchToProps = (dispatch) => ({ diff --git a/client/coral-admin/src/reducers/assets.js b/client/coral-admin/src/reducers/assets.js index 03f0be9bb..bb8751973 100644 --- a/client/coral-admin/src/reducers/assets.js +++ b/client/coral-admin/src/reducers/assets.js @@ -1,35 +1,40 @@ -import {Map, List, fromJS} from 'immutable'; import * as actions from '../constants/assets'; +import update from 'immutability-helper'; -const initialState = Map({ - byId: Map(), - ids: List(), - assets: List() -}); +const initialState = { + byId: {}, + ids: [], + assets: [] +}; export default function assets (state = initialState, action) { switch (action.type) { - case actions.FETCH_ASSETS_SUCCESS: - return replaceAssets(action, state); + case actions.FETCH_ASSETS_SUCCESS: { + const assets = action.assets.reduce((prev, curr) => { + prev[curr.id] = curr; + return prev; + }, {}); + + return update(state, { + byId: {$set: assets}, + count: {$set: action.count}, + ids: {$set: Object.keys(assets)}, + }); + } case actions.UPDATE_ASSET_STATE_REQUEST: - return state - .setIn(['byId', action.id, 'closedAt'], action.closedAt); + return update(state, { + byId: { + [action.id]: { + closedAt: {$set: action.closedAt}, + }, + }, + }); case actions.UPDATE_ASSETS: - return state - .set('assets', List(action.assets)); + return update(state, { + assets: {$set: action.assets}, + }); default: return state; } } -const replaceAssets = (action, state) => { - const assets = fromJS(action.assets.reduce((prev, curr) => { - prev[curr.id] = curr; - return prev; - }, {})); - - return state - .set('byId', assets) - .set('count', action.count) - .set('ids', List(assets.keys())); -}; diff --git a/client/coral-admin/src/reducers/auth.js b/client/coral-admin/src/reducers/auth.js index a60d73cec..a9c7f520e 100644 --- a/client/coral-admin/src/reducers/auth.js +++ b/client/coral-admin/src/reducers/auth.js @@ -1,43 +1,63 @@ -import {Map} from 'immutable'; import * as actions from '../constants/auth'; -const initialState = Map({ +const initialState = { loggedIn: false, user: null, loginError: null, loginMaxExceeded: false, passwordRequestSuccess: null -}); +}; export default function auth (state = initialState, action) { switch (action.type) { case actions.CHECK_LOGIN_REQUEST: - return state - .set('loadingUser', true); + return { + ...state, + loadingUser: true, + }; case actions.CHECK_LOGIN_FAILURE: - return state - .set('loggedIn', false) - .set('loadingUser', false) - .set('user', null); + return { + ...state, + loggedIn: false, + loadingUser: false, + user: null, + }; case actions.CHECK_LOGIN_SUCCESS: - return state - .set('loggedIn', true) - .set('loadingUser', false) - .set('user', action.user); + return { + ...state, + loggedIn: true, + loadingUser: false, + user: action.user, + }; case actions.LOGOUT: return initialState; case actions.LOGIN_SUCCESS: - return state.set('loginMaxExceeded', false).set('loginError', null); + return { + ...state, + loginMaxExceeded: false, + loginError: null, + }; case actions.LOGIN_FAILURE: - return state.set('loginError', action.message); + return { + ...state, + loginError: action.message, + }; case actions.FETCH_FORGOT_PASSWORD_REQUEST: - return state.set('passwordRequestSuccess', null); + return { + ...state, + passwordRequestSuccess: null, + }; case actions.FETCH_FORGOT_PASSWORD_SUCCESS: - return state.set('passwordRequestSuccess', 'If you have a registered account, a password reset link was sent to that email.'); + return { + ...state, + passwordRequestSuccess: 'If you have a registered account, a password reset link was sent to that email.', + }; case actions.LOGIN_MAXIMUM_EXCEEDED: - return state - .set('loginMaxExceeded', true) - .set('loginError', action.message); + return { + ...state, + loginMaxExceeded: true, + loginError: action.message, + }; default : return state; } diff --git a/client/coral-admin/src/reducers/community.js b/client/coral-admin/src/reducers/community.js index 3fba504f8..9f724c8d6 100644 --- a/client/coral-admin/src/reducers/community.js +++ b/client/coral-admin/src/reducers/community.js @@ -1,5 +1,3 @@ -import {Map} from 'immutable'; - import { FETCH_COMMENTERS_REQUEST, FETCH_COMMENTERS_FAILURE, @@ -13,8 +11,8 @@ import { HIDE_REJECT_USERNAME_DIALOG } from '../constants/community'; -const initialState = Map({ - community: Map(), +const initialState = { + community: {}, isFetchingPeople: false, errorPeople: '', accounts: [], @@ -22,72 +20,87 @@ const initialState = Map({ ascPeople: false, totalPagesPeople: 0, pagePeople: 0, - user: Map({}), + user: {}, banDialog: false, rejectUsernameDialog: false -}); +}; export default function community (state = initialState, action) { switch (action.type) { case FETCH_COMMENTERS_REQUEST : - return state - .set('isFetchingPeople', true); + return { + ...state, + isFetchingPeople: true, + }; case FETCH_COMMENTERS_FAILURE : - return state - .set('isFetchingPeople', false) - .set('errorPeople', action.error); - + return { + ...state, + isFetchingPeople: false, + errorPeople: action.error, + }; case FETCH_COMMENTERS_SUCCESS : { const {accounts, type, page, count, limit, totalPages, ...rest} = action; // eslint-disable-line - return state - .merge({ - isFetchingPeople: false, - errorPeople: '', - pagePeople: page, - countPeople: count, - limitPeople: limit, - totalPagesPeople: totalPages, - ...rest - }) - .set('accounts', accounts); // Sets to normal array + return { + ...state, + isFetchingPeople: false, + errorPeople: '', + pagePeople: page, + countPeople: count, + limitPeople: limit, + totalPagesPeople: totalPages, + ...rest, + accounts, // Sets to normal array + }; } case SET_ROLE : { - const commenters = state.get('accounts'); + const commenters = state.accounts; const idx = commenters.findIndex((el) => el.id === action.id); commenters[idx].roles[0] = action.role; - return state.set('accounts', commenters.map((id) => id)); + return { + ...state, + accounts: commenters.map((id) => id), + }; } case SET_COMMENTER_STATUS: { - const commenters = state.get('accounts'); + const commenters = state.accounts; const idx = commenters.findIndex((el) => el.id === action.id); commenters[idx].status = action.status; - return state.set('accounts', commenters.map((id) => id)); + return { + ...state, + accounts: commenters.map((id) => id), + }; } case SORT_UPDATE : - return state - .set('fieldPeople', action.sort.field) - .set('ascPeople', !state.get('ascPeople')); + return { + ...state, + fieldPeople: action.sort.field, + ascPeople: !state.ascPeople, + }; case HIDE_BANUSER_DIALOG: - return state - .set('banDialog', false); + return { + ...state, + banDialog: false, + }; case SHOW_BANUSER_DIALOG: - return state - .merge({ - user: Map(action.user), - banDialog: true - }); + return { + ...state, + user: action.user, + banDialog: true, + }; case HIDE_REJECT_USERNAME_DIALOG: - return state - .set('rejectUsernameDialog', false); + return { + ...state, + rejectUsernameDialog: false, + }; case SHOW_REJECT_USERNAME_DIALOG: - return state - .merge({ - user: Map(action.user), - rejectUsernameDialog: true - }); + return { + ...state, + user: action.user, + rejectUsernameDialog: true + }; default : return state; } diff --git a/client/coral-admin/src/reducers/config.js b/client/coral-admin/src/reducers/config.js index a5d11d6eb..310a198ed 100644 --- a/client/coral-admin/src/reducers/config.js +++ b/client/coral-admin/src/reducers/config.js @@ -1,15 +1,16 @@ -import {Map} from 'immutable'; - import * as actions from '../actions/config'; -const initialState = Map({ - data: Map({}) -}); +const initialState = { + data: {} +}; export default function config (state = initialState, action) { switch (action.type) { case actions.CONFIG_UPDATED: - return state.set('data', Map(action.data)); + return { + ...state, + data: action.data, + }; default: return state; } diff --git a/client/coral-admin/src/reducers/install.js b/client/coral-admin/src/reducers/install.js index 1f0f079ff..c6e85c69f 100644 --- a/client/coral-admin/src/reducers/install.js +++ b/client/coral-admin/src/reducers/install.js @@ -1,30 +1,29 @@ -import {Map, List} from 'immutable'; - import * as actions from '../constants/install'; +import update from 'immutability-helper'; -const initialState = Map({ +const initialState = { isLoading: false, - data: Map({ - settings: Map({ + data: { + settings: { organizationName: '', - domains: Map({ - whitelist: List() - }) - }), - user: Map({ + domains: { + whitelist: [], + } + }, + user: { username: '', email: '', password: '', confirmPassword: '' - }) - }), - errors: Map({ + } + }, + errors: { organizationName: '', username: '', email: '', password: '', confirmPassword: '' - }), + }, showErrors: false, hasError: false, error: null, @@ -44,57 +43,91 @@ const initialState = Map({ installRequest: null, installRequestError: null, alreadyInstalled: false -}); +}; export default function install (state = initialState, action) { switch (action.type) { case actions.NEXT_STEP: - return state - .set('step', state.get('step') + 1); + return { + ...state, + step: state.step + 1, + }; case actions.PREVIOUS_STEP: - return state - .set('step', state.get('step') - 1); + return { + ...state, + step: state.step - 1, + }; case actions.GO_TO_STEP: - return state - .set('step', action.step); + return { + ...state, + step: action.step, + }; case actions.UPDATE_PERMITTED_DOMAINS_SETTINGS: - return state - .setIn(['data', 'settings', 'domains', 'whitelist'], action.value); + return update(state, { + data: { + settings: { + domains: { + whitelist: {$set: action.value}, + }, + }, + }, + }); case actions.UPDATE_FORMDATA_SETTINGS: - return state - .setIn(['data', 'settings', action.name], action.value); + return update(state, { + data: { + settings: { + [action.name]: {$set: action.value}, + }, + }, + }); case actions.UPDATE_FORMDATA_USER: - return state - .setIn(['data', 'user', action.name], action.value); + return update(state, { + data: { + user: { + [action.name]: {$set: action.value}, + }, + }, + }); case actions.HAS_ERROR: - return state - .merge({ - hasError: true, - showErrors: true - }); + return { + ...state, + hasError: true, + showErrors: true, + }; case actions.ADD_ERROR: - return state - .setIn(['errors', action.name], action.error); + return update(state, { + errors: { + [action.name]: {$set: action.error}, + }, + }); case actions.CLEAR_ERRORS: - return state - .set('errors', Map()); + return { + ...state, + errors: {}, + }; case actions.INSTALL_REQUEST: - return state - .set('isLoading', true); + return { + ...state, + isLoading: true, + }; case actions.INSTALL_SUCCESS: - return state - .set('isLoading', false) - .set('installRequest', 'SUCCESS'); + return { + ...state, + isLoading: false, + installRequest: 'SUCCESS', + }; case actions.INSTALL_FAILURE: - return state - .merge({ - isLoading: false, - installRequest: 'FAILURE', - installRequestError: action.error - }); + return { + ...state, + isLoading: false, + installRequest: 'FAILURE', + installRequestError: action.error + }; case actions.CHECK_INSTALL_SUCCESS: - return state - .set('alreadyInstalled', action.installed); + return { + ...state, + alreadyInstalled: action.installed, + }; default : return state; } diff --git a/client/coral-admin/src/reducers/moderation.js b/client/coral-admin/src/reducers/moderation.js index a0cba1f5f..e0a608488 100644 --- a/client/coral-admin/src/reducers/moderation.js +++ b/client/coral-admin/src/reducers/moderation.js @@ -1,37 +1,54 @@ -import {fromJS} from 'immutable'; import * as actions from '../constants/moderation'; -const initialState = fromJS({ +const initialState = { singleView: false, modalOpen: false, storySearchVisible: false, storySearchString: '', shortcutsNoteVisible: window.localStorage.getItem('coral:shortcutsNote') || 'show', sortOrder: 'REVERSE_CHRONOLOGICAL', -}); +}; export default function moderation (state = initialState, action) { switch (action.type) { case actions.MODERATION_CLEAR_STATE: return initialState; case actions.TOGGLE_MODAL: - return state - .set('modalOpen', action.open); + return { + ...state, + modalOpen: action.open, + }; case actions.SINGLE_VIEW: - return state - .set('singleView', !state.get('singleView')); + return { + ...state, + singleView: !state.singleView, + }; case actions.HIDE_SHORTCUTS_NOTE: - return state - .set('shortcutsNoteVisible', 'hide'); + return { + ...state, + shortcutsNoteVisible: 'hide', + }; case actions.SHOW_STORY_SEARCH: - return state.set('storySearchVisible', true); + return { + ...state, + storySearchVisible: true, + }; case actions.HIDE_STORY_SEARCH: - return state.set('storySearchVisible', false); + return { + ...state, + storySearchVisible: false, + }; case actions.STORY_SEARCH_CHANGE_VALUE: - return state.set('storySearchString', action.value); + return { + ...state, + storySearchString: action.value, + }; case actions.SET_SORT_ORDER: - return state.set('sortOrder', action.order); - default : + return { + ...state, + sortOrder: action.order, + }; + default: return state; } } diff --git a/client/coral-admin/src/reducers/settings.js b/client/coral-admin/src/reducers/settings.js index 67907b327..6e29ba719 100644 --- a/client/coral-admin/src/reducers/settings.js +++ b/client/coral-admin/src/reducers/settings.js @@ -1,5 +1,5 @@ -import {Map, List} from 'immutable'; import * as actions from '../actions/settings'; +import update from 'immutability-helper'; // this is initialized here because // currently you have to reload the dashboard to get new stats @@ -10,63 +10,80 @@ const DASHBOARD_WINDOW_MINUTES = 5; let then = new Date(); then.setMinutes(then.getMinutes() - DASHBOARD_WINDOW_MINUTES); -const initialState = Map({ - wordlist: Map({ - banned: List(), - suspect: List() - }), +const initialState = { + wordlist: { + banned: [], + suspect: [] + }, dashboardWindowStart: then.toISOString(), dashboardWindowEnd: new Date().toISOString(), - domains: Map({ - whitelist: List() - }), + domains: { + whitelist: [] + }, saveSettingsError: null, fetchSettingsError: null, fetchingSettings: false -}); +}; export default function settings (state = initialState, action) { switch (action.type) { case actions.SETTINGS_LOADING: - return state - .set('fetchingSettings', true) - .set('fetchSettingsError', null); + return { + ...state, + fetchingSettings: true, + fetchSettingsError: null, + }; case actions.SETTINGS_RECEIVED: - return state.merge({ + return { + ...state, fetchingSettings: false, fetchSettingsError: null, ...action.settings - }); + }; case actions.SETTINGS_FETCH_ERROR: - return state - .set('fetchingSettings', false) - .set('fetchSettingsError', action.error); + return { + ...state, + fetchingSettings: false, + fetchSettingsError: action.error, + }; case actions.SETTINGS_UPDATED: - return state.merge({ + return { + ...state, fetchingSettings: false, fetchSettingsError: null, ...action.settings - }); + }; case actions.SAVE_SETTINGS_LOADING: - return state - .set('fetchingSettings', true) - .set('saveSettingsError', null); + return { + ...state, + fetchingSettings: true, + saveSettingsError: null, + }; case actions.SAVE_SETTINGS_SUCCESS: - return state.merge({ + return { + ...state, fetchingSettings: false, fetchSettingsError: null, ...action.settings - }); + }; case actions.SAVE_SETTINGS_FAILED: - return state - .set('fetchingSettings', false) - .set('fetchSettingsError', action.error); + return { + ...state, + fetchingSettings: false, + fetchSettingsError: action.error, + }; case actions.WORDLIST_UPDATED: - return state - .setIn(['wordlist', action.listName], action.list); + return update(state, { + wordList: { + [action.listName]: {$set: action.list}, + } + }); case actions.DOMAINLIST_UPDATED: - return state - .setIn(['domains', action.listName], action.list); + return update(state, { + domains: { + [action.listName]: {$set: action.list}, + } + }); default: return state; } diff --git a/client/coral-admin/src/routes/Community/containers/Community.js b/client/coral-admin/src/routes/Community/containers/Community.js index a9a164ac9..b04fc0281 100644 --- a/client/coral-admin/src/routes/Community/containers/Community.js +++ b/client/coral-admin/src/routes/Community/containers/Community.js @@ -87,8 +87,8 @@ export const withCommunityQuery = withQuery(gql` }); const mapStateToProps = (state) => ({ - community: state.community.toJS(), - currentUser: state.auth.toJS().user, + community: state.community, + currentUser: state.auth.user, }); const mapDispatchToProps = (dispatch) => diff --git a/client/coral-admin/src/routes/Community/containers/Table.js b/client/coral-admin/src/routes/Community/containers/Table.js index 8bd5395e4..5d4e4914f 100644 --- a/client/coral-admin/src/routes/Community/containers/Table.js +++ b/client/coral-admin/src/routes/Community/containers/Table.js @@ -23,7 +23,7 @@ class TableContainer extends Component { } const mapStateToProps = (state) => ({ - commenters: state.community.get('accounts'), + commenters: state.community.accounts, }); const mapDispatchToProps = (dispatch) => diff --git a/client/coral-admin/src/routes/Configure/containers/Configure.js b/client/coral-admin/src/routes/Configure/containers/Configure.js index c68b39e0c..aff1e57b2 100644 --- a/client/coral-admin/src/routes/Configure/containers/Configure.js +++ b/client/coral-admin/src/routes/Configure/containers/Configure.js @@ -23,8 +23,8 @@ class ConfigureContainer extends Component { } const mapStateToProps = (state) => ({ - auth: state.auth.toJS(), - settings: state.settings.toJS() + auth: state.auth, + settings: state.settings }); const mapDispatchToProps = (dispatch) => diff --git a/client/coral-admin/src/routes/Dashboard/containers/Dashboard.js b/client/coral-admin/src/routes/Dashboard/containers/Dashboard.js index ea1fb2b6c..74faedcf7 100644 --- a/client/coral-admin/src/routes/Dashboard/containers/Dashboard.js +++ b/client/coral-admin/src/routes/Dashboard/containers/Dashboard.js @@ -54,8 +54,8 @@ export const witDashboardQuery = withQuery(gql` const mapStateToProps = (state) => { return { - settings: state.settings.toJS(), - moderation: state.moderation.toJS() + settings: state.settings, + moderation: state.moderation }; }; diff --git a/client/coral-admin/src/routes/Install/containers/Install.js b/client/coral-admin/src/routes/Install/containers/Install.js index 5dcb35705..442127e47 100644 --- a/client/coral-admin/src/routes/Install/containers/Install.js +++ b/client/coral-admin/src/routes/Install/containers/Install.js @@ -35,7 +35,7 @@ InstallContainer.contextTypes = { }; const mapStateToProps = (state) => ({ - install: state.install.toJS() + install: state.install }); const mapDispatchToProps = (dispatch) => diff --git a/client/coral-admin/src/routes/Moderation/containers/Moderation.js b/client/coral-admin/src/routes/Moderation/containers/Moderation.js index d7f62491d..d5ca5c840 100644 --- a/client/coral-admin/src/routes/Moderation/containers/Moderation.js +++ b/client/coral-admin/src/routes/Moderation/containers/Moderation.js @@ -376,9 +376,9 @@ const withQueueCountPolling = withQuery(gql` }); const mapStateToProps = (state) => ({ - moderation: state.moderation.toJS(), - settings: state.settings.toJS(), - auth: state.auth.toJS(), + moderation: state.moderation, + settings: state.settings, + auth: state.auth, }); const mapDispatchToProps = (dispatch) => ({ diff --git a/client/coral-admin/src/routes/Stories/containers/Stories.js b/client/coral-admin/src/routes/Stories/containers/Stories.js index 450efcf74..f645f310c 100644 --- a/client/coral-admin/src/routes/Stories/containers/Stories.js +++ b/client/coral-admin/src/routes/Stories/containers/Stories.js @@ -12,7 +12,7 @@ class StoriesContainer extends Component { } const mapStateToProps = (state) => ({ - assets: state.assets.toJS() + assets: state.assets }); const mapDispatchToProps = (dispatch) => diff --git a/plugins/talk-plugin-featured-comments/client/containers/ModSubscription.js b/plugins/talk-plugin-featured-comments/client/containers/ModSubscription.js index 85e0d8fe2..82f323530 100644 --- a/plugins/talk-plugin-featured-comments/client/containers/ModSubscription.js +++ b/plugins/talk-plugin-featured-comments/client/containers/ModSubscription.js @@ -91,7 +91,7 @@ const COMMENT_UNFEATURED_SUBSCRIPTION = gql` `; const mapStateToProps = (state) => ({ - user: state.auth.toJS().user, + user: state.auth.user, }); export default connect(mapStateToProps, null)(ModSubscription); From cdfef8c9896da81538fe321713c2bd1ee21b63cc Mon Sep 17 00:00:00 2001 From: Chi Vinh Le Date: Mon, 14 Aug 2017 16:56:51 +0700 Subject: [PATCH 5/8] Remove immutable --- package.json | 1 - yarn.lock | 17 +++-------------- 2 files changed, 3 insertions(+), 15 deletions(-) diff --git a/package.json b/package.json index bb0a724ac..a0a1efe17 100644 --- a/package.json +++ b/package.json @@ -180,7 +180,6 @@ "hammerjs": "^2.0.8", "history": "^3.0.0", "ignore-styles": "^5.0.1", - "immutable": "^3.8.1", "imports-loader": "^0.7.1", "istanbul": "^1.1.0-alpha.1", "jsdom": "^9.8.3", diff --git a/yarn.lock b/yarn.lock index a5d73a756..9244af84f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3996,11 +3996,11 @@ iconv-lite@0.4.13: version "0.4.13" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.13.tgz#1f88aba4ab0b1508e8312acc39345f36e992e2f2" -iconv-lite@0.4.15, iconv-lite@^0.4.5, iconv-lite@~0.4.13: +iconv-lite@0.4.15: version "0.4.15" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.15.tgz#fe265a218ac6a57cfe854927e9d04c19825eddeb" -iconv-lite@^0.4.17: +iconv-lite@^0.4.17, iconv-lite@^0.4.5, iconv-lite@~0.4.13: version "0.4.18" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.18.tgz#23d8656b16aae6742ac29732ea8f0336a4789cf2" @@ -4034,10 +4034,6 @@ immutability-helper@^2.2.0: dependencies: invariant "^2.2.0" -immutable@^3.8.1: - version "3.8.1" - resolved "https://registry.yarnpkg.com/immutable/-/immutable-3.8.1.tgz#200807f11ab0f72710ea485542de088075f68cd2" - imports-loader@^0.7.1: version "0.7.1" resolved "https://registry.yarnpkg.com/imports-loader/-/imports-loader-0.7.1.tgz#f204b5f34702a32c1db7d48d89d5e867a0441253" @@ -7869,14 +7865,7 @@ string-width@^1.0.1, string-width@^1.0.2: is-fullwidth-code-point "^1.0.0" strip-ansi "^3.0.0" -string-width@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.0.0.tgz#635c5436cc72a6e0c387ceca278d4e2eec52687e" - dependencies: - is-fullwidth-code-point "^2.0.0" - strip-ansi "^3.0.0" - -string-width@^2.1.0: +string-width@^2.0.0, string-width@^2.1.0: version "2.1.1" resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" dependencies: From 06cf33521fb26ae4e6ba9b6fdf5eb2a858161b8f Mon Sep 17 00:00:00 2001 From: Chi Vinh Le Date: Mon, 14 Aug 2017 19:40:52 +0700 Subject: [PATCH 6/8] Remove immutable-js traces from docs --- docs/_docs/03-05-client-architecture.md | 9 --------- 1 file changed, 9 deletions(-) diff --git a/docs/_docs/03-05-client-architecture.md b/docs/_docs/03-05-client-architecture.md index 6bb44bfad..6996811c5 100644 --- a/docs/_docs/03-05-client-architecture.md +++ b/docs/_docs/03-05-client-architecture.md @@ -6,7 +6,6 @@ permalink: /docs/architecture/client ## The Stack - [React](#react) - [Redux](#redux) - - [ImmutableJS](#immutablejs) ## The Architecture @@ -96,14 +95,6 @@ We use [Apollo](http://www.apollodata.com/) to handle graph requests and handle ## Redux We use [Redux](http://redux.js.org/) to handle the auth state. - -## ImmutableJS -We use Immutable JS to maintain our state immutable. -We found some really good tradeoffs while building Talk. - -[How to use ImmutableJS and how we use it with Talk](https://facebook.github.io/immutable-js/docs/#/) - - ## Test [How we do testing at Coral with Talk]({{ "/docs/development/tools" | absolute_url }}) From e71eb28e4234e7dedb27a7fdf890a724070ec6e1 Mon Sep 17 00:00:00 2001 From: Wyatt Johnson Date: Tue, 15 Aug 2017 14:19:58 -0600 Subject: [PATCH 7/8] added base path support for router, spelling fixes --- app.js | 192 ++++------------------ config.js | 12 +- docs/_docs/00-01-faq.md | 69 ++++++-- docs/_docs/01-02-install-docker.md | 5 +- docs/_docs/01-03-install-source.md | 2 +- docs/_docs/01-05-install-microservices.md | 2 +- docs/_docs/02-01-configuration.md | 19 ++- docs/_docs/02-02-secrets.md | 2 +- docs/_docs/03-05-client-architecture.md | 2 +- docs/_docs/04-01-plugins.md | 2 +- docs/_docs/04-02-plugins-quickstart.md | 2 +- docs/_docs/04-03-plugins-client.md | 2 +- docs/_docs/04-04-plugins-server.md | 6 +- middleware/pubsub.js | 13 ++ routes/index.js | 142 +++++++++++++++- 15 files changed, 275 insertions(+), 197 deletions(-) create mode 100644 middleware/pubsub.js diff --git a/app.js b/app.js index 0be7a1830..9abb921ae 100644 --- a/app.js +++ b/app.js @@ -3,71 +3,43 @@ const bodyParser = require('body-parser'); const morgan = require('morgan'); const path = require('path'); const helmet = require('helmet'); -const authentication = require('./middleware/authentication'); -const {passport} = require('./services/passport'); -const plugins = require('./services/plugins'); -const pubsub = require('./services/pubsub'); -const i18n = require('./services/i18n'); -const enabled = require('debug').enabled; -const errors = require('./errors'); -const {createGraphOptions} = require('./graph'); -const apollo = require('graphql-server-express'); -const accepts = require('accepts'); const compression = require('compression'); const cookieParser = require('cookie-parser'); -const {ROOT_URL} = require('./config'); +const {ROOT_URL, ROOT_URL_MOUNT_PATH} = require('./config'); +const routes = require('./routes'); +const debug = require('debug')('talk:app'); +const {URL} = require('url'); const app = express(); -// Middleware declarations. +//============================================================================== +// APPLICATION WIDE MIDDLEWARE +//============================================================================== // Add the logging middleware only if we aren't testing. -if (app.get('env') !== 'test') { +if (process.env.NODE_ENV !== 'test') { app.use(morgan('dev')); } -//============================================================================== -// APP MIDDLEWARE -//============================================================================== - +// Trust the first proxy in front of us, this will enable us to trust the fact +// that SSL was terminated correctly. app.set('trust proxy', 1); -// We disable frameward on helmet to allow crossdomain injection of the embed +// Enable a suite of security good practices through helmet. We disable +// frameguard to allow crossdomain injection of the embed. app.use(helmet({ - frameguard: false + frameguard: false, })); + +// Compress the responses if appropriate. app.use(compression()); + +// Parse the cookies on the request. app.use(cookieParser()); + +// Parse the body json if it's there. app.use(bodyParser.json()); -//============================================================================== -// STATIC FILES -//============================================================================== - -// If the application is in production mode, then add gzip rewriting for the -// content. -if (process.env.NODE_ENV === 'production') { - app.get('*.js', (req, res, next) => { - const accept = accepts(req); - if (accept.encoding(['gzip']) === 'gzip') { - - // Adjsut the headers on the request by adding a content type header - // because express won't be able to detect the mime-type with the .gz - // extension and we need to decalre support for the gzip encoding. - res.set('Content-Type', 'application/javascript'); - res.set('Content-Encoding', 'gzip'); - - // Rewrite the url so that the gzip version will be served instead. - req.url = `${req.url}.gz`; - } - - next(); - }); -} - -app.use('/client', express.static(path.join(__dirname, 'dist'))); -app.use('/public', express.static(path.join(__dirname, 'public'))); - //============================================================================== // VIEW CONFIGURATION //============================================================================== @@ -75,124 +47,30 @@ app.use('/public', express.static(path.join(__dirname, 'public'))); app.set('views', path.join(__dirname, 'views')); app.set('view engine', 'ejs'); -// Set the BASE_URL as the ROOT_URL. -app.locals.BASE_URL = ROOT_URL; -if (app.locals.BASE_URL[app.locals.BASE_URL.length - 1] !== '/') { - app.locals.BASE_URL += '/'; -} - -//============================================================================== -// PASSPORT MIDDLEWARE -//============================================================================== - -const passportDebug = require('debug')('talk:passport'); - -// Install the passport plugins. -plugins.get('server', 'passport').forEach((plugin) => { - passportDebug(`added plugin '${plugin.plugin.name}'`); - - // Pass the passport.js instance to the plugin to allow it to inject it's - // functionality. - plugin.passport(passport); -}); - -// Setup the PassportJS Middleware. -app.use(passport.initialize()); - -// Attach the authentication middleware, this will be responsible for decoding -// (if present) the JWT on the request. -app.use('/api', authentication); - -const pubsubClient = pubsub.createClientFactory(); - -// To handle dependancy injection safer, we inject the pubsub handle onto the -// request object. -app.use('/api', (req, res, next) => { - - // Attach the pubsub handle to the requests. - req.pubsub = pubsubClient(); - - // Forward on the request. - next(); -}); - -//============================================================================== -// GraphQL Router -//============================================================================== - -// GraphQL endpoint. -app.use('/api/v1/graph/ql', apollo.graphqlExpress(createGraphOptions)); - -// Only include the graphiql tool if we aren't in production mode. -if (app.get('env') !== 'production') { - - // Interactive graphiql interface. - app.use('/api/v1/graph/iql', (req, res) => { - res.render('graphiql', { - endpointURL: '/api/v1/graph/ql' - }); - }); - - // GraphQL documention. - app.get('/admin/docs', (req, res) => { - res.render('admin/docs'); - }); - -} - //============================================================================== // ROUTES //============================================================================== -app.use('/', require('./routes')); +// Set the BASE_URL as the ROOT_URL, here we derive the root url by ensuring +// that it ends in a `/`. +const BASE_URL = ROOT_URL && ROOT_URL.length > 0 && ROOT_URL[ROOT_URL.length - 1] === '/' ? ROOT_URL : `${ROOT_URL}/`; -//============================================================================== -// ERROR HANDLING -//============================================================================== +// The BASE_PATH is simply the path component of the BASE_URL. +const BASE_PATH = new URL(BASE_URL).pathname; -// Catch 404 and forward to error handler. -app.use((req, res, next) => { - next(errors.ErrNotFound); -}); +// The MOUNT_PATH is derived from the BASE_PATH, if it is provided and enabled. +// This will mount all the application routes onto it. +const MOUNT_PATH = ROOT_URL_MOUNT_PATH ? BASE_PATH : '/'; -// General error handler. Respond with the message and error if we have it while -// returning a status code that makes sense. -app.use('/api', (err, req, res, next) => { - if (err !== errors.ErrNotFound) { - if (app.get('env') !== 'test' || enabled('talk:errors')) { - console.error(err); - } - } +// Apply the BASE_PATH, BASE_URL, and MOUNT_PATH on the app.locals, which will +// make them available on the templates and the routers. +app.locals.BASE_URL = BASE_URL; +app.locals.BASE_PATH = BASE_PATH; +app.locals.MOUNT_PATH = MOUNT_PATH; - if (err instanceof errors.APIError) { - res.status(err.status).json({ - message: err.message, - error: err - }); - } else { - res.status(500).json({}); - } -}); +debug(`mounting routes on the ${MOUNT_PATH} path`); -app.use('/', (err, req, res, next) => { - if (err !== errors.ErrNotFound) { - console.error(err); - } - - i18n.init(req); - - if (err instanceof errors.APIError) { - res.status(err.status); - res.render('error', { - message: err.message, - error: app.get('env') === 'development' ? err : {} - }); - } else { - res.render('error', { - message: err.message, - error: app.get('env') === 'development' ? err : {} - }); - } -}); +// Actually apply the routes. +app.use(MOUNT_PATH, routes); module.exports = app; diff --git a/config.js b/config.js index 4472af26e..a2335822d 100644 --- a/config.js +++ b/config.js @@ -85,6 +85,10 @@ const CONFIG = { // The URL for this Talk Instance as viewable from the outside. ROOT_URL: process.env.TALK_ROOT_URL || null, + // ROOT_URL_MOUNT_PATH when TRUE will extract the pathname from the + // TALK_ROOT_URL and use it to mount the paths on. + ROOT_URL_MOUNT_PATH: process.env.TALK_ROOT_URL_MOUNT_PATH === 'TRUE', + // The keepalive timeout (in ms) that should be used to send keep alive // messages through the websocket to keep the socket alive. KEEP_ALIVE: process.env.TALK_KEEP_ALIVE || '30s', @@ -129,6 +133,10 @@ const CONFIG = { // CONFIG VALIDATION //============================================================================== +if (CONFIG.ROOT_URL_MOUNT_PATH && !CONFIG.ROOT_URL) { + throw new Error('TALK_ROOT_URL must be specified if TALK_ROOT_URL_MOUNT_PATH is set to TRUE'); +} + if (process.env.NODE_ENV === 'test' && !CONFIG.ROOT_URL) { CONFIG.ROOT_URL = 'http://localhost:3000'; } else if (!CONFIG.ROOT_URL) { @@ -169,14 +177,14 @@ if (CONFIG.JWT_DISABLE_ISSUER) { // External database url's //------------------------------------------------------------------------------ -// Reset the mongo url in the event it hasn't been overrided and we are in a +// Reset the mongo url in the event it hasn't been overridden and we are in a // testing environment. Every new mongo instance comes with a test database by // default, this is consistent with common testing and use case practices. if (process.env.NODE_ENV === 'test' && !CONFIG.MONGO_URL) { CONFIG.MONGO_URL = 'mongodb://localhost/test'; } -// Reset the redis url in the event it hasn't been overrided and we are in a +// Reset the redis url in the event it hasn't been overridden and we are in a // testing environment. if (process.env.NODE_ENV === 'test' && !CONFIG.REDIS_URL) { CONFIG.REDIS_URL = 'redis://localhost/1'; diff --git a/docs/_docs/00-01-faq.md b/docs/_docs/00-01-faq.md index cdaf5dbf3..01172f84a 100644 --- a/docs/_docs/00-01-faq.md +++ b/docs/_docs/00-01-faq.md @@ -5,39 +5,63 @@ permalink: /docs/faq/ ### How are new stories/assets added to Talk? Is there an API? -There are three ways that new assets can make their way into Talk: _just in time_, _active_ and manual. +There are three ways that new assets can make their way into Talk: + +- Just in time +- Active +- Manual #### Just in Time asset creation -Talk ships with a _just in time_ mechanism that works out of the box without integration with any CMS or manual work needed. +Talk ships with a _just in time_ mechanism that works out of the box without +integration with any CMS or manual work needed. The _just in time_ flow looks like this: * Request comes in for a stream on an asset that doesn't yet exist. * Talk screens the domain against the domain whitelist, fails if doesn't pass. * Then, concurrently - * Talk creates a new asset record and returns the stream data (which will be empty) + * Talk creates a new asset record and returns the stream data (which will be + empty) * Schedules a job to scrape the new page and fill in asset information. -The scraping mechanism utilizes [metascraper](https://www.npmjs.com/package/metascraper) and is queued using the [Que](https://www.npmjs.com/package/kue). If your Talk deployments is configured to run separate job worker cluster, scraping will be performed by them. +The scraping mechanism utilizes +[metascraper](https://www.npmjs.com/package/metascraper) and is queued using the +[Que](https://www.npmjs.com/package/kue). If your Talk deployments is configured +to run separate job worker cluster, scraping will be performed by them. #### Active (or push based) asset creation -If tighter CMS integration is required to push custom data into assets and/or keep data in sync as changes are made in a CMS an _active_ push based workflow must be implemented. +If tighter CMS integration is required to push custom data into assets and/or +keep data in sync as changes are made in a CMS an _active_ push based workflow +must be implemented. -This is an ideal candidate for a plugin. If you are interested in working on it, please [contact us](https://coralproject.net/contact)! +This is an ideal candidate for a plugin. If you are interested in working on it, +please [contact us](https://coralproject.net/contact)! #### Manual asset creation -Sometimes you want to load a lot of assets into the database. The most common use case for this is populating the database during an initial installation. We recommend writing a script that transforms the data from it's source and inserts it into the _assets_ collection. +Sometimes you want to load a lot of assets into the database. The most common +use case for this is populating the database during an initial installation. We +recommend writing a script that transforms the data from it's source and inserts +it into the _assets_ collection. -For current schema information, please see the [asset model](https://github.com/coralproject/talk/blob/master/models/asset.js). +For current schema information, please see the +[asset model](https://github.com/coralproject/talk/blob/master/models/asset.js). ### Where are your http API docs? -Coral relies on GraphQL for the vast majority of it's client <-> server communication. All core queries, mutations and subscriptions are defined along with types and comments in our central [TypeDef](https://github.com/coralproject/talk/blob/master/graph/typeDefs.graphql). For plugin graph api typedefs, see each plugin's `/server/` directory. +Coral relies on GraphQL for the vast majority of it's client and server +communication. All core queries, mutations and subscriptions are defined along +with types and comments in our central +[TypeDef](https://github.com/coralproject/talk/blob/master/graph/typeDefs.graphql). +For plugin graph api typedefs, see each plugin's `/server/` directory. -In addition, Talk Server ships with [GraphiQL](https://github.com/graphql/graphiql). GraphiQL provides a full data layer IDE including interactive documentation. The autocompletes and documentation are populated from introspection meaning that Core _and plugin_ apis will be fully explorable. +In addition, Talk Server ships with +[GraphiQL](https://github.com/graphql/graphiql). GraphiQL provides a full data +layer IDE including interactive documentation. The autocomplete and +documentation are populated from introspection meaning that Core _and plugin_ +apis can be explored. To access GraphiQL: @@ -46,22 +70,33 @@ To access GraphiQL: ### Where is documentation for a specific component? -We strive for clear inline documentation across our codebase, but have gaps. Contributions to documentation would be greatly appreciated and is a great way to start contributing to the project! +We strive for clear inline documentation across our codebase, but have gaps. +Contributions to documentation would be greatly appreciated and is a great way +to start contributing to the project! -If you are considering changing a core component (aka, one that is not in a plugin), you are entering the realm of a core developer. We strongly ask that you reach out the coral team before forking and changing core code. We are glad to help talk through your product need and come up with a strategy for implementing as a plugin, or working with you to extend the plugin API for your use case. +If you are considering changing a core component (aka, one that is not in a +plugin), you are entering the realm of a core developer. We strongly ask that +you reach out the coral team before forking and changing core code. We are glad +to help talk through your product need and come up with a strategy for +implementing as a plugin, or working with you to extend the plugin API for your +use case. ### How do I contribute to these docs? -Contributions to the docs are much appreciated and a great way to get involved in the project. +Contributions to the docs are much appreciated and a great way to get involved +in the project. -Fork the Talk repo, clone it locally (no need to go through the install from source process), then: +Fork the Talk repo, clone it locally (no need to go through the install from +source process), then: ``` cd docs docker run --rm --volume=$PWD:/srv/jekyll -p 127.0.0.1:4000:4000 -it jekyll/jekyll:pages jekyll serve ``` -You can edit the files in docs with any editor and view the live updates in a browser by hitting From the docs directory. -Then visit: [http://127.0.0.1:4000/talk/](http://127.0.0.1:4000/talk/). +You can edit the files in docs with any editor and view the live updates in a +browser by hitting From the docs directory. Then visit: +[http://127.0.0.1:4000/talk/](http://127.0.0.1:4000/talk/). -Once you've made the changes, file a PR back to the `coralproject/talk` repo. +Once you've made the changes, file a PR back to the `coralproject/talk` +repository. diff --git a/docs/_docs/01-02-install-docker.md b/docs/_docs/01-02-install-docker.md index cf9c1fa0e..533f9ec16 100644 --- a/docs/_docs/01-02-install-docker.md +++ b/docs/_docs/01-02-install-docker.md @@ -11,8 +11,9 @@ Available as [coralproject/talk](https://hub.docker.com/r/coralproject/talk/) on Images are tagged using the following notation: -- `x` (where `x` is the major version number): any minor or patch updates will be included in this. If you're ok getting - new features occasionally and all the bug fixes, this is the tag for you. +- `x` (where `x` is the major version number): any minor or patch updates will + be included in this. If you're ok getting new features occasionally and all + the bug fixes, this is the tag for you. - `x.y` (where `y` is the minor version number): any patch updates will be included with this tag. If you like getting fixes and having features change only when you want, this is the tag for you. **(recommended)** diff --git a/docs/_docs/01-03-install-source.md b/docs/_docs/01-03-install-source.md index 3f05478ce..66dc94db6 100644 --- a/docs/_docs/01-03-install-source.md +++ b/docs/_docs/01-03-install-source.md @@ -36,7 +36,7 @@ git clone https://github.com/coralproject/talk.git We now have to install the dependencies and build the static assets. ```bash -# Install package dependancies +# Install package dependencies yarn # Build static files diff --git a/docs/_docs/01-05-install-microservices.md b/docs/_docs/01-05-install-microservices.md index 8e80a13ac..dc5ff112e 100644 --- a/docs/_docs/01-05-install-microservices.md +++ b/docs/_docs/01-05-install-microservices.md @@ -70,7 +70,7 @@ independently. Each variety of process can always have just enough resources. An install that heavily utilizes the jobs queue could see delays in http service because of heavy jobs processes and/or delays in the execution of jobs processes -due to increased server load as a result of Node's single thread infrustructure. +due to increased server load as a result of Node's single thread infrastructure. ## Deployment Methodologies diff --git a/docs/_docs/02-01-configuration.md b/docs/_docs/02-01-configuration.md index 74bd8972e..ac614fdb1 100644 --- a/docs/_docs/02-01-configuration.md +++ b/docs/_docs/02-01-configuration.md @@ -55,11 +55,18 @@ These are only used during the webpack build. ### Server - `TALK_ROOT_URL` (*required*) - root url of the installed application externally - available in the format: `://` without the path. + available in the format: `://:/`. - `TALK_KEEP_ALIVE` (_optional_) - The keepalive timeout that should be used to send keep alive messages through the websocket to keep the socket alive. (Default `30s`) - `TALK_INSTALL_LOCK` (_optional for dynamic setup_) - When `TRUE`, disables the dynamic setup endpoint. (Default `FALSE`) +#### Advanced + +- `TALK_ROOT_URL_MOUNT_PATH` (_optional_) - when set to `TRUE`, the routes will + be mounted onto the `` component of the `TALK_ROOT_URL`. You would + use this when your upstream proxy cannot strip the prefix from the url. + (Default `FALSE`) + ### Word Filter - `TALK_DISABLE_AUTOFLAG_SUSPECT_WORDS` (_optional_) When `TRUE`, disables flagging of comments that match the suspect word filter. (Default `FALSE`) @@ -110,7 +117,7 @@ will be used: "iss": TALK_JWT_ISSUER, // *optional* if TALK_JWT_DISABLE_ISSUER === 'TRUE', *required* otherwise [TALK_JWT_USER_ID_CLAIM]: "", // *required* the id of the user - // Note, if TALK_JWT_USER_ID_CLAIM contains '.', it will be used to deliniate an object, for example + // Note, if TALK_JWT_USER_ID_CLAIM contains '.', it will be used to delineate an object, for example // `user.id` would store it like: `{user: {id}}` } ``` @@ -135,8 +142,8 @@ will be used: ### Trust -Trust can automoderate comments based on user history. By specifying this -option, the beheviour can be changed to offer different results. +Trust can auto-moderate comments based on user history. By specifying this +option, the behavior can be changed to offer different results. - `TRUST_THRESHOLDS` (_optional_) - configure the reliability thresholds for flagging and commenting. (Default `comment:-1,-1;flag:-1,-1`) @@ -150,10 +157,10 @@ The form of the environment variable: The default could be read as: - When a commenter has one comment rejected, their next comment must be - premoderated once in order to post freely again. If they instead get rejected + pre-moderated once in order to post freely again. If they instead get rejected again, then they must have two of their comments approved in order to get added back to the queue. -- At the moment of writing, beheviour is not attached to the flagging +- At the moment of writing, behavior is not attached to the flagging reliability, but it is recorded. ### Cache diff --git a/docs/_docs/02-02-secrets.md b/docs/_docs/02-02-secrets.md index c974c350f..42c1946b6 100644 --- a/docs/_docs/02-02-secrets.md +++ b/docs/_docs/02-02-secrets.md @@ -67,7 +67,7 @@ for more details. ## Authentication Types -Talk also supports two methods of providing authenticationd details. +Talk also supports two methods of providing authentication details. - Single key: this is used when your secrets do not need to be rotated. - Multiple keys: this is used when you expect to rotate your secrets. diff --git a/docs/_docs/03-05-client-architecture.md b/docs/_docs/03-05-client-architecture.md index 6bb44bfad..6392c20d0 100644 --- a/docs/_docs/03-05-client-architecture.md +++ b/docs/_docs/03-05-client-architecture.md @@ -32,7 +32,7 @@ It basically consist in having two types of components: ### Container Components * __How things work__ * They don’t have markup nor styles -* They provide data and behaviour to Presentational or Container Components +* They provide data and behavior to Presentational or Container Components * They connect via `react-redux`’s `connect()` to the state. * They `mapStateToProps` the state to the Presentational Container. * They `mapDispatchToProps` to send actions to the Presentational Container. diff --git a/docs/_docs/04-01-plugins.md b/docs/_docs/04-01-plugins.md index 8b9b1c99a..3182f6e3a 100644 --- a/docs/_docs/04-01-plugins.md +++ b/docs/_docs/04-01-plugins.md @@ -82,7 +82,7 @@ need to reconcile the plugins and build the static assets: # get plugin dependancies and remote plugins ./bin/cli plugins reconcile -# build staic assets (including enabled client side plugins) +# build static assets (including enabled client side plugins) yarn build ``` diff --git a/docs/_docs/04-02-plugins-quickstart.md b/docs/_docs/04-02-plugins-quickstart.md index cb2d2800b..ec5e23fef 100644 --- a/docs/_docs/04-02-plugins-quickstart.md +++ b/docs/_docs/04-02-plugins-quickstart.md @@ -236,6 +236,6 @@ Once you've taken this step, anyone can register your plugin into their Talk ser ### Publish to version control -This plugin is open source, so I'm also going to [publish it to github](https://github.com/jde/talk-plugin-asset-manager/commit/66b626caa85cb8030b3ddaa7c1a4821bf01e350a) and [cut a release](https://github.com/jde/talk-plugin-asset-manager/releases/tag/v0.1) that mirrors the npm relese. +This plugin is open source, so I'm also going to [publish it to github](https://github.com/jde/talk-plugin-asset-manager/commit/66b626caa85cb8030b3ddaa7c1a4821bf01e350a) and [cut a release](https://github.com/jde/talk-plugin-asset-manager/releases/tag/v0.1) that mirrors the npm release. ## Done! diff --git a/docs/_docs/04-03-plugins-client.md b/docs/_docs/04-03-plugins-client.md index 052b90873..511ccde6c 100644 --- a/docs/_docs/04-03-plugins-client.md +++ b/docs/_docs/04-03-plugins-client.md @@ -218,7 +218,7 @@ Basic settings can be added via json configuration in a plugin. * Default value * Variable name -#### Advanced Custom Configuration (low prioritiy) +#### Advanced Custom Configuration (low priority) Users can inject configuration interfaces that they create into the configuration allowing for more advanced configuration. diff --git a/docs/_docs/04-04-plugins-server.md b/docs/_docs/04-04-plugins-server.md index 6f204e6b8..13df5564f 100644 --- a/docs/_docs/04-04-plugins-server.md +++ b/docs/_docs/04-04-plugins-server.md @@ -220,7 +220,7 @@ when a valid token is provided but a user can't be found in the database that matches the provided id. The function is async, and should return the user object that was created in the -database, or null if the user wasn't found. The `jwt` paramenter of the object +database, or null if the user wasn't found. The `jwt` parameter of the object is the unpacked token, while `token` is the original jwt token string. ### Routes @@ -314,14 +314,14 @@ module.exports = { const {passport} = require('services/passport'); /** - * Facebook auth endpoint, this will redirect the user immediatly to facebook + * Facebook auth endpoint, this will redirect the user immediately to facebook * for authorization. */ router.get('/facebook', passport.authenticate('facebook', {display: 'popup', authType: 'rerequest', scope: ['public_profile']})); /** * Facebook callback endpoint, this will send the user a html page designed to - * send back the user credentials upon sucesfull login. + * send back the user credentials upon successful login. */ router.get('/facebook/callback', (req, res, next) => { diff --git a/middleware/pubsub.js b/middleware/pubsub.js new file mode 100644 index 000000000..15f7c51a2 --- /dev/null +++ b/middleware/pubsub.js @@ -0,0 +1,13 @@ +const pubsub = require('../services/pubsub'); +const pubsubClient = pubsub.createClientFactory(); + +// To handle dependancy injection safer, we inject the pubsub handle onto the +// request object. +module.exports = (req, res, next) => { + + // Attach the pubsub handle to the requests. + req.pubsub = pubsubClient(); + + // Forward on the request. + next(); +}; diff --git a/routes/index.js b/routes/index.js index f1992e83d..f83dd37cb 100644 --- a/routes/index.js +++ b/routes/index.js @@ -2,12 +2,45 @@ const express = require('express'); const path = require('path'); const plugins = require('../services/plugins'); const debug = require('debug')('talk:routes'); +const authentication = require('../middleware/authentication'); +const {passport} = require('../services/passport'); +const pubsub = require('../middleware/pubsub'); +const i18n = require('../services/i18n'); +const enabled = require('debug').enabled; +const errors = require('../errors'); +const {createGraphOptions} = require('../graph'); +const accepts = require('accepts'); +const apollo = require('graphql-server-express'); const router = express.Router(); -router.use('/api/v1', require('./api')); -router.use('/admin', require('./admin')); -router.use('/embed', require('./embed')); +//============================================================================== +// STATIC FILES +//============================================================================== + +// If the application is in production mode, then add gzip rewriting for the +// content. +if (process.env.NODE_ENV === 'production') { + router.get('*.js', (req, res, next) => { + const accept = accepts(req); + if (accept.encoding(['gzip']) === 'gzip') { + + // Adjsut the headers on the request by adding a content type header + // because express won't be able to detect the mime-type with the .gz + // extension and we need to decalre support for the gzip encoding. + res.set('Content-Type', 'application/javascript'); + res.set('Content-Encoding', 'gzip'); + + // Rewrite the url so that the gzip version will be served instead. + req.url = `${req.url}.gz`; + } + + next(); + }); +} + +router.use('/client', express.static(path.join(__dirname, 'dist'))); +router.use('/public', express.static(path.join(__dirname, 'public'))); /** * Serves a file based on a relative path. @@ -21,6 +54,60 @@ router.get('/embed.js', serveFile('../dist/embed.js')); router.get('/embed.js.gz', serveFile('../dist/embed.js.gz')); router.get('/embed.js.map', serveFile('../dist/embed.js.map')); +//============================================================================== +// PASSPORT MIDDLEWARE +//============================================================================== + +const passportDebug = require('debug')('talk:passport'); + +// Install the passport plugins. +plugins.get('server', 'passport').forEach((plugin) => { + passportDebug(`added plugin '${plugin.plugin.name}'`); + + // Pass the passport.js instance to the plugin to allow it to inject it's + // functionality. + plugin.passport(passport); +}); + +// Setup the PassportJS Middleware. +router.use(passport.initialize()); + +// Attach the authentication middleware, this will be responsible for decoding +// (if present) the JWT on the request. +router.use('/api', authentication, pubsub); + +//============================================================================== +// GraphQL Router +//============================================================================== + +// GraphQL endpoint. +router.use('/api/v1/graph/ql', apollo.graphqlExpress(createGraphOptions)); + +// Only include the graphiql tool if we aren't in production mode. +if (process.env.NODE_ENV !== 'production') { + + // Interactive graphiql interface. + router.use('/api/v1/graph/iql', (req, res) => { + res.render('graphiql', { + endpointURL: `${req.locals.BASE_URL}api/v1/graph/ql` + }); + }); + + // GraphQL documention. + router.get('/admin/docs', (req, res) => { + res.render('admin/docs'); + }); + +} + +//============================================================================== +// ROUTES +//============================================================================== + +router.use('/api/v1', require('./api')); +router.use('/admin', require('./admin')); +router.use('/embed', require('./embed')); + if (process.env.NODE_ENV !== 'production') { router.use('/assets', require('./assets')); @@ -43,4 +130,53 @@ plugins.get('server', 'router').forEach((plugin) => { plugin.router(router); }); +//============================================================================== +// ERROR HANDLING +//============================================================================== + +// Catch 404 and forward to error handler. +router.use((req, res, next) => { + next(errors.ErrNotFound); +}); + +// General api error handler. Respond with the message and error if we have it +// while returning a status code that makes sense. +router.use('/api', (err, req, res, next) => { + if (err !== errors.ErrNotFound) { + if (process.env.NODE_ENV !== 'test' || enabled('talk:errors')) { + console.error(err); + } + } + + if (err instanceof errors.APIError) { + res.status(err.status).json({ + message: err.message, + error: err + }); + } else { + res.status(500).json({}); + } +}); + +router.use('/', (err, req, res, next) => { + if (err !== errors.ErrNotFound) { + console.error(err); + } + + i18n.init(req); + + if (err instanceof errors.APIError) { + res.status(err.status); + res.render('error', { + message: err.message, + error: process.env.NODE_ENV === 'development' ? err : {} + }); + } else { + res.render('error', { + message: err.message, + error: process.env.NODE_ENV === 'development' ? err : {} + }); + } +}); + module.exports = router; From 8afbe9d30d6527e58256ceb00279f74a7e8c068c Mon Sep 17 00:00:00 2001 From: Wyatt Johnson Date: Tue, 15 Aug 2017 14:40:12 -0600 Subject: [PATCH 8/8] applied path fixes to template --- views/admin.ejs | 2 +- views/admin/confirm-email.ejs | 2 +- views/admin/docs.ejs | 2 +- views/admin/password-reset.ejs | 2 +- views/article.ejs | 3 +-- views/articles.ejs | 2 +- views/embed/stream.ejs | 2 +- 7 files changed, 7 insertions(+), 8 deletions(-) diff --git a/views/admin.ejs b/views/admin.ejs index 0a7c1c218..2aa23aa58 100644 --- a/views/admin.ejs +++ b/views/admin.ejs @@ -42,6 +42,6 @@
- + diff --git a/views/admin/confirm-email.ejs b/views/admin/confirm-email.ejs index 20d213fa2..ed23d3dc8 100644 --- a/views/admin/confirm-email.ejs +++ b/views/admin/confirm-email.ejs @@ -71,7 +71,7 @@ $('.error-console').removeClass('active'); $.ajax({ - url: '/api/v1/account/email/verify', + url: '<%= BASE_PATH %>api/v1/account/email/verify', contentType: 'application/json', method: 'POST', data: JSON.stringify({token: location.hash.replace('#', '')}) diff --git a/views/admin/docs.ejs b/views/admin/docs.ejs index de288e055..d13dff064 100644 --- a/views/admin/docs.ejs +++ b/views/admin/docs.ejs @@ -28,6 +28,6 @@
- + diff --git a/views/admin/password-reset.ejs b/views/admin/password-reset.ejs index 704b5213d..f02e5b6f8 100644 --- a/views/admin/password-reset.ejs +++ b/views/admin/password-reset.ejs @@ -117,7 +117,7 @@ } $.ajax({ - url: '/api/v1/account/password/reset', + url: '<%= BASE_PATH %>api/v1/account/password/reset', contentType: 'application/json', method: 'PUT', data: JSON.stringify({password: password, token: location.hash.replace('#', '')}) diff --git a/views/article.ejs b/views/article.ejs index 098a95524..f0a3dad98 100644 --- a/views/article.ejs +++ b/views/article.ejs @@ -17,7 +17,6 @@ } <%= title %> -
@@ -25,7 +24,7 @@

<%= body %>

Admin - All Assets

- +