fix(user/settings): Add theme server validations
This commit is contained in:
@ -2,7 +2,6 @@ import analyticsEpic from './analytics-epic.js';
|
|||||||
import errEpic from './err-epic.js';
|
import errEpic from './err-epic.js';
|
||||||
import hardGoToEpic from './hard-go-to-epic.js';
|
import hardGoToEpic from './hard-go-to-epic.js';
|
||||||
import mouseTrapEpic from './mouse-trap-epic.js';
|
import mouseTrapEpic from './mouse-trap-epic.js';
|
||||||
import nightModeEpic from './night-mode-epic.js';
|
|
||||||
import titleEpic from './title-epic.js';
|
import titleEpic from './title-epic.js';
|
||||||
|
|
||||||
export default [
|
export default [
|
||||||
@ -10,6 +9,5 @@ export default [
|
|||||||
errEpic,
|
errEpic,
|
||||||
hardGoToEpic,
|
hardGoToEpic,
|
||||||
mouseTrapEpic,
|
mouseTrapEpic,
|
||||||
nightModeEpic,
|
|
||||||
titleEpic
|
titleEpic
|
||||||
];
|
];
|
||||||
|
@ -12,9 +12,11 @@ import { createSelector } from 'reselect';
|
|||||||
import fetchUserEpic from './fetch-user-epic.js';
|
import fetchUserEpic from './fetch-user-epic.js';
|
||||||
import updateMyCurrentChallengeEpic from './update-my-challenge-epic.js';
|
import updateMyCurrentChallengeEpic from './update-my-challenge-epic.js';
|
||||||
import fetchChallengesEpic from './fetch-challenges-epic.js';
|
import fetchChallengesEpic from './fetch-challenges-epic.js';
|
||||||
|
import nightModeEpic from './night-mode-epic.js';
|
||||||
|
|
||||||
import { createFilesMetaCreator } from '../files';
|
import { createFilesMetaCreator } from '../files';
|
||||||
import { updateThemeMetacreator, entitiesSelector } from '../entities';
|
import { updateThemeMetacreator, entitiesSelector } from '../entities';
|
||||||
|
import { utils } from '../Flash/redux';
|
||||||
import { types as challenges } from '../routes/Challenges/redux';
|
import { types as challenges } from '../routes/Challenges/redux';
|
||||||
import { challengeToFiles } from '../routes/Challenges/utils';
|
import { challengeToFiles } from '../routes/Challenges/utils';
|
||||||
|
|
||||||
@ -23,8 +25,9 @@ import ns from '../ns.json';
|
|||||||
import { themes, invertTheme } from '../../utils/themes.js';
|
import { themes, invertTheme } from '../../utils/themes.js';
|
||||||
|
|
||||||
export const epics = [
|
export const epics = [
|
||||||
fetchUserEpic,
|
|
||||||
fetchChallengesEpic,
|
fetchChallengesEpic,
|
||||||
|
fetchUserEpic,
|
||||||
|
nightModeEpic,
|
||||||
updateMyCurrentChallengeEpic
|
updateMyCurrentChallengeEpic
|
||||||
];
|
];
|
||||||
|
|
||||||
@ -48,7 +51,7 @@ export const types = createTypes([
|
|||||||
|
|
||||||
// night mode
|
// night mode
|
||||||
'toggleNightMode',
|
'toggleNightMode',
|
||||||
'postThemeComplete'
|
createAsyncTypes('postTheme')
|
||||||
], ns);
|
], ns);
|
||||||
|
|
||||||
const throwIfUndefined = () => {
|
const throwIfUndefined = () => {
|
||||||
@ -130,6 +133,7 @@ export const createErrorObservable = error => Observable.just({
|
|||||||
type: types.handleError,
|
type: types.handleError,
|
||||||
error
|
error
|
||||||
});
|
});
|
||||||
|
// use sparingly
|
||||||
// doActionOnError(
|
// doActionOnError(
|
||||||
// actionCreator: (() => Action|Null)
|
// actionCreator: (() => Action|Null)
|
||||||
// ) => (error: Error) => Observable[Action]
|
// ) => (error: Error) => Observable[Action]
|
||||||
@ -147,9 +151,18 @@ export const toggleNightMode = createAction(
|
|||||||
(username, theme) => updateThemeMetacreator(username, invertTheme(theme))
|
(username, theme) => updateThemeMetacreator(username, invertTheme(theme))
|
||||||
);
|
);
|
||||||
export const postThemeComplete = createAction(
|
export const postThemeComplete = createAction(
|
||||||
types.postThemeComplete,
|
types.postTheme.complete,
|
||||||
null,
|
null,
|
||||||
updateThemeMetacreator
|
utils.createFlashMetaAction
|
||||||
|
);
|
||||||
|
|
||||||
|
export const postThemeError = createAction(
|
||||||
|
types.postTheme.error,
|
||||||
|
null,
|
||||||
|
(username, theme, err) => ({
|
||||||
|
...updateThemeMetacreator(username, invertTheme(theme)),
|
||||||
|
...utils.createFlashMetaAction(err)
|
||||||
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
const defaultState = {
|
const defaultState = {
|
||||||
|
@ -6,14 +6,12 @@ import store from 'store';
|
|||||||
import { themes } from '../../utils/themes.js';
|
import { themes } from '../../utils/themes.js';
|
||||||
import { postJSON$ } from '../../utils/ajax-stream.js';
|
import { postJSON$ } from '../../utils/ajax-stream.js';
|
||||||
import {
|
import {
|
||||||
types,
|
csrfSelector,
|
||||||
|
|
||||||
postThemeComplete,
|
postThemeComplete,
|
||||||
createErrorObservable,
|
postThemeError,
|
||||||
|
|
||||||
themeSelector,
|
themeSelector,
|
||||||
usernameSelector,
|
types,
|
||||||
csrfSelector
|
usernameSelector
|
||||||
} from './index.js';
|
} from './index.js';
|
||||||
|
|
||||||
function persistTheme(theme) {
|
function persistTheme(theme) {
|
||||||
@ -33,7 +31,8 @@ export default function nightModeEpic(
|
|||||||
::ofType(
|
::ofType(
|
||||||
types.fetchUser.complete,
|
types.fetchUser.complete,
|
||||||
types.toggleNightMode,
|
types.toggleNightMode,
|
||||||
types.postThemeComplete
|
types.postTheme.complete,
|
||||||
|
types.postTheme.error
|
||||||
)
|
)
|
||||||
.map(_.flow(getState, themeSelector))
|
.map(_.flow(getState, themeSelector))
|
||||||
// catch existing night mode users
|
// catch existing night mode users
|
||||||
@ -54,9 +53,10 @@ export default function nightModeEpic(
|
|||||||
const theme = themeSelector(getState());
|
const theme = themeSelector(getState());
|
||||||
const username = usernameSelector(getState());
|
const username = usernameSelector(getState());
|
||||||
return postJSON$('/update-my-theme', { _csrf, theme })
|
return postJSON$('/update-my-theme', { _csrf, theme })
|
||||||
.pluck('updatedTo')
|
.map(postThemeComplete)
|
||||||
.map(theme => postThemeComplete(username, theme))
|
.catch(err => {
|
||||||
.catch(createErrorObservable);
|
return Observable.of(postThemeError(username, theme, err));
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
return Observable.merge(toggleBodyClass, postThemeEpic);
|
return Observable.merge(toggleBodyClass, postThemeEpic);
|
||||||
|
@ -703,9 +703,7 @@ module.exports = function(User) {
|
|||||||
);
|
);
|
||||||
return Promise.reject(err);
|
return Promise.reject(err);
|
||||||
}
|
}
|
||||||
return this.update$({ theme })
|
return this.update$({ theme }).toPromise();
|
||||||
.map({ updatedTo: theme })
|
|
||||||
.toPromise();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// deprecated. remove once live
|
// deprecated. remove once live
|
||||||
|
@ -5,6 +5,7 @@ import {
|
|||||||
createValidatorErrorHandler
|
createValidatorErrorHandler
|
||||||
} from '../utils/middleware';
|
} from '../utils/middleware';
|
||||||
import supportedLanguages from '../../common/utils/supported-languages.js';
|
import supportedLanguages from '../../common/utils/supported-languages.js';
|
||||||
|
import { themes } from '../../common/utils/themes.js';
|
||||||
|
|
||||||
export default function settingsController(app) {
|
export default function settingsController(app) {
|
||||||
const api = app.loopback.Router();
|
const api = app.loopback.Router();
|
||||||
@ -79,22 +80,29 @@ export default function settingsController(app) {
|
|||||||
updateMyCurrentChallenge
|
updateMyCurrentChallenge
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const updateMyThemeValidators = [
|
||||||
|
check('theme')
|
||||||
|
.isIn(Object.keys(themes))
|
||||||
|
.withMessage('Theme is invalid.')
|
||||||
|
];
|
||||||
function updateMyTheme(req, res, next) {
|
function updateMyTheme(req, res, next) {
|
||||||
req.checkBody('theme', 'Theme is invalid.').isLength({ min: 4 });
|
|
||||||
const { body: { theme } } = req;
|
const { body: { theme } } = req;
|
||||||
const errors = req.validationErrors(true);
|
|
||||||
if (errors) {
|
|
||||||
return res.status(403).json({ errors });
|
|
||||||
}
|
|
||||||
if (req.user.theme === theme) {
|
if (req.user.theme === theme) {
|
||||||
return res.json({ msg: 'Theme already set' });
|
return res.sendFlash('info', 'Theme already set');
|
||||||
}
|
}
|
||||||
return req.user.updateTheme('' + theme)
|
return req.user.updateTheme(theme)
|
||||||
.then(
|
.then(
|
||||||
data => res.json(data),
|
() => res.sendFlash('info', 'Your theme has been updated'),
|
||||||
next
|
next
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
api.post(
|
||||||
|
'/update-my-theme',
|
||||||
|
ifNoUser401,
|
||||||
|
updateMyThemeValidators,
|
||||||
|
createValidatorErrorHandler('errors'),
|
||||||
|
updateMyTheme
|
||||||
|
);
|
||||||
|
|
||||||
api.post(
|
api.post(
|
||||||
'/toggle-available-for-hire',
|
'/toggle-available-for-hire',
|
||||||
@ -131,11 +139,6 @@ export default function settingsController(app) {
|
|||||||
ifNoUser401,
|
ifNoUser401,
|
||||||
updateMyLang
|
updateMyLang
|
||||||
);
|
);
|
||||||
api.post(
|
|
||||||
'/update-my-theme',
|
|
||||||
ifNoUser401,
|
|
||||||
updateMyTheme
|
|
||||||
);
|
|
||||||
|
|
||||||
app.use(api);
|
app.use(api);
|
||||||
}
|
}
|
||||||
|
@ -19,13 +19,14 @@ export function wrapHandledError(err, {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// for use with express-validator error formatter
|
// for use with express-validator error formatter
|
||||||
export const createValidatorErrorFormatter = (type, redirectTo, status) =>
|
export const createValidatorErrorFormatter = (type, redirectTo) =>
|
||||||
({ msg }) => wrapHandledError(
|
({ msg }) => wrapHandledError(
|
||||||
new Error(msg),
|
new Error(msg),
|
||||||
{
|
{
|
||||||
type,
|
type,
|
||||||
message: msg,
|
message: msg,
|
||||||
redirectTo,
|
redirectTo,
|
||||||
status
|
// we default to 400 as these are malformed requests
|
||||||
|
status: 400
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
Reference in New Issue
Block a user