commit
6b9d557896
6 changed files with 112 additions and 10 deletions
|
|
@ -1,9 +1,13 @@
|
||||||
import { Meteor } from 'meteor/meteor';
|
import { Meteor } from 'meteor/meteor';
|
||||||
import { check, Match } from 'meteor/check';
|
import { check } from 'meteor/check';
|
||||||
|
import { Accounts } from 'meteor/accounts-base';
|
||||||
import editProfile from './edit-profile';
|
import editProfile from './edit-profile';
|
||||||
import rateLimit from '../../../modules/rate-limit';
|
import rateLimit from '../../../modules/rate-limit';
|
||||||
|
|
||||||
Meteor.methods({
|
Meteor.methods({
|
||||||
|
'users.sendVerificationEmail': function usersResendVerification() {
|
||||||
|
return Accounts.sendVerificationEmail(this.userId);
|
||||||
|
},
|
||||||
'users.editProfile': function usersEditProfile(profile) {
|
'users.editProfile': function usersEditProfile(profile) {
|
||||||
check(profile, {
|
check(profile, {
|
||||||
emailAddress: String,
|
emailAddress: String,
|
||||||
|
|
@ -25,6 +29,7 @@ Meteor.methods({
|
||||||
|
|
||||||
rateLimit({
|
rateLimit({
|
||||||
methods: [
|
methods: [
|
||||||
|
'users.resendVerification',
|
||||||
'users.editProfile',
|
'users.editProfile',
|
||||||
],
|
],
|
||||||
limit: 5,
|
limit: 5,
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,18 @@ const emailTemplates = Accounts.emailTemplates;
|
||||||
emailTemplates.siteName = name;
|
emailTemplates.siteName = name;
|
||||||
emailTemplates.from = from;
|
emailTemplates.from = from;
|
||||||
|
|
||||||
|
emailTemplates.verifyEmail = {
|
||||||
|
subject() {
|
||||||
|
return `[${name}] Verify Your Email Address`;
|
||||||
|
},
|
||||||
|
text(user, url) {
|
||||||
|
const userEmail = user.emails[0].address;
|
||||||
|
const urlWithoutHash = url.replace('#/', '');
|
||||||
|
if (Meteor.isDevelopment) console.info(`Verify Email Link: ${urlWithoutHash}`); // eslint-disable-line
|
||||||
|
return `Hey, ${user.profile.name.first}! Welcome to ${name}.\n\nTo verify your email address (${userEmail}), click the link below:\n\n${urlWithoutHash}\n\nIf you feel something is wrong, please contact our support team: ${email}.`;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
emailTemplates.resetPassword = {
|
emailTemplates.resetPassword = {
|
||||||
subject() {
|
subject() {
|
||||||
return `[${name}] Reset Your Password`;
|
return `[${name}] Reset Your Password`;
|
||||||
|
|
@ -16,13 +28,7 @@ emailTemplates.resetPassword = {
|
||||||
text(user, url) {
|
text(user, url) {
|
||||||
const userEmail = user.emails[0].address;
|
const userEmail = user.emails[0].address;
|
||||||
const urlWithoutHash = url.replace('#/', '');
|
const urlWithoutHash = url.replace('#/', '');
|
||||||
|
if (Meteor.isDevelopment) console.info(`Reset Password Link: ${urlWithoutHash}`); // eslint-disable-line
|
||||||
if (Meteor.isDevelopment) console.info(`Reset Password Link: ${urlWithoutHash}`);
|
return `A password reset has been requested for the account related to this address (${userEmail}).\n\nTo reset the password, visit the following link: \n\n${urlWithoutHash}\n\n If you did not request this reset, please ignore this email. If you feel something is wrong, please contact our support team: ${email}.`;
|
||||||
|
|
||||||
return `A password reset has been requested for the account related to this
|
|
||||||
address (${userEmail}). To reset the password, visit the following link:
|
|
||||||
\n\n${urlWithoutHash}\n\n If you did not request this reset, please ignore
|
|
||||||
this email. If you feel something is wrong, please contact our support team:
|
|
||||||
${email}.`;
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,13 @@
|
||||||
|
/* eslint-disable jsx-a11y/no-href*/
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { BrowserRouter as Router, Switch, Route } from 'react-router-dom';
|
import { BrowserRouter as Router, Switch, Route } from 'react-router-dom';
|
||||||
import { Grid } from 'react-bootstrap';
|
import { Grid, Alert, Button } from 'react-bootstrap';
|
||||||
import { Meteor } from 'meteor/meteor';
|
import { Meteor } from 'meteor/meteor';
|
||||||
import { createContainer } from 'meteor/react-meteor-data';
|
import { createContainer } from 'meteor/react-meteor-data';
|
||||||
import { Roles } from 'meteor/alanning:roles';
|
import { Roles } from 'meteor/alanning:roles';
|
||||||
|
import { Bert } from 'meteor/themeteorchef:bert';
|
||||||
import Navigation from '../../components/Navigation/Navigation';
|
import Navigation from '../../components/Navigation/Navigation';
|
||||||
import Authenticated from '../../components/Authenticated/Authenticated';
|
import Authenticated from '../../components/Authenticated/Authenticated';
|
||||||
import Public from '../../components/Public/Public';
|
import Public from '../../components/Public/Public';
|
||||||
|
|
@ -16,6 +19,7 @@ import EditDocument from '../../pages/EditDocument/EditDocument';
|
||||||
import Signup from '../../pages/Signup/Signup';
|
import Signup from '../../pages/Signup/Signup';
|
||||||
import Login from '../../pages/Login/Login';
|
import Login from '../../pages/Login/Login';
|
||||||
import Logout from '../../pages/Logout/Logout';
|
import Logout from '../../pages/Logout/Logout';
|
||||||
|
import VerifyEmail from '../../pages/VerifyEmail/VerifyEmail';
|
||||||
import RecoverPassword from '../../pages/RecoverPassword/RecoverPassword';
|
import RecoverPassword from '../../pages/RecoverPassword/RecoverPassword';
|
||||||
import ResetPassword from '../../pages/ResetPassword/ResetPassword';
|
import ResetPassword from '../../pages/ResetPassword/ResetPassword';
|
||||||
import Profile from '../../pages/Profile/Profile';
|
import Profile from '../../pages/Profile/Profile';
|
||||||
|
|
@ -27,9 +31,20 @@ import ExamplePage from '../../pages/ExamplePage/ExamplePage';
|
||||||
|
|
||||||
import './App.scss';
|
import './App.scss';
|
||||||
|
|
||||||
|
const handleResendVerificationEmail = (emailAddress) => {
|
||||||
|
Meteor.call('users.sendVerificationEmail', (error) => {
|
||||||
|
if (error) {
|
||||||
|
Bert.alert(error.reason, 'danger');
|
||||||
|
} else {
|
||||||
|
Bert.alert(`Check ${emailAddress} for a verification link!`, 'success');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
const App = props => (
|
const App = props => (
|
||||||
<Router>
|
<Router>
|
||||||
{!props.loading ? <div className="App">
|
{!props.loading ? <div className="App">
|
||||||
|
{props.userId && !props.emailVerified ? <Alert className="verify-email text-center"><p>Hey friend! Can you <strong>verify your email address</strong> ({props.emailAddress}) for us? <Button bsStyle="link" onClick={() => handleResendVerificationEmail(props.emailAddress)} href="#">Re-send verification email</Button></p></Alert> : ''}
|
||||||
<Navigation {...props} />
|
<Navigation {...props} />
|
||||||
<Grid>
|
<Grid>
|
||||||
<Switch>
|
<Switch>
|
||||||
|
|
@ -42,6 +57,7 @@ const App = props => (
|
||||||
<Public path="/signup" component={Signup} {...props} />
|
<Public path="/signup" component={Signup} {...props} />
|
||||||
<Public path="/login" component={Login} {...props} />
|
<Public path="/login" component={Login} {...props} />
|
||||||
<Public path="/logout" component={Logout} {...props} />
|
<Public path="/logout" component={Logout} {...props} />
|
||||||
|
<Route name="verify-email" path="/verify-email/:token" component={VerifyEmail} />
|
||||||
<Route name="recover-password" path="/recover-password" component={RecoverPassword} />
|
<Route name="recover-password" path="/recover-password" component={RecoverPassword} />
|
||||||
<Route name="reset-password" path="/reset-password/:token" component={ResetPassword} />
|
<Route name="reset-password" path="/reset-password/:token" component={ResetPassword} />
|
||||||
<Route name="terms" path="/terms" component={Terms} />
|
<Route name="terms" path="/terms" component={Terms} />
|
||||||
|
|
@ -55,8 +71,16 @@ const App = props => (
|
||||||
</Router>
|
</Router>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
App.defaultProps = {
|
||||||
|
userId: '',
|
||||||
|
emailAddress: '',
|
||||||
|
};
|
||||||
|
|
||||||
App.propTypes = {
|
App.propTypes = {
|
||||||
loading: PropTypes.bool.isRequired,
|
loading: PropTypes.bool.isRequired,
|
||||||
|
userId: PropTypes.string,
|
||||||
|
emailAddress: PropTypes.string,
|
||||||
|
emailVerified: PropTypes.bool.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
const getUserName = name => ({
|
const getUserName = name => ({
|
||||||
|
|
@ -78,5 +102,8 @@ export default createContainer(() => {
|
||||||
authenticated: !loggingIn && !!userId,
|
authenticated: !loggingIn && !!userId,
|
||||||
name: name || emailAddress,
|
name: name || emailAddress,
|
||||||
roles: !loading && Roles.getRolesForUser(userId),
|
roles: !loading && Roles.getRolesForUser(userId),
|
||||||
|
userId,
|
||||||
|
emailAddress,
|
||||||
|
emailVerified: user ? user && user.emails && user.emails[0].verified : true,
|
||||||
};
|
};
|
||||||
}, App);
|
}, App);
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,23 @@
|
||||||
|
@import '../../stylesheets/colors';
|
||||||
|
|
||||||
.App > .container {
|
.App > .container {
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.App .verify-email {
|
||||||
|
margin-bottom: 0;
|
||||||
|
padding: 0;
|
||||||
|
border-top: none;
|
||||||
|
border-bottom: 1px solid #e7e7e7;
|
||||||
|
background: #fff;
|
||||||
|
color: $gray-dark;
|
||||||
|
border-radius: 0;
|
||||||
|
|
||||||
|
p {
|
||||||
|
padding: 19px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ import React from 'react';
|
||||||
import { Row, Col, FormGroup, ControlLabel, Button } from 'react-bootstrap';
|
import { Row, Col, FormGroup, ControlLabel, Button } from 'react-bootstrap';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
|
import { Meteor } from 'meteor/meteor';
|
||||||
import { Accounts } from 'meteor/accounts-base';
|
import { Accounts } from 'meteor/accounts-base';
|
||||||
import { Bert } from 'meteor/themeteorchef:bert';
|
import { Bert } from 'meteor/themeteorchef:bert';
|
||||||
import OAuthLoginButtons from '../../components/OAuthLoginButtons/OAuthLoginButtons';
|
import OAuthLoginButtons from '../../components/OAuthLoginButtons/OAuthLoginButtons';
|
||||||
|
|
@ -71,6 +72,7 @@ class Signup extends React.Component {
|
||||||
if (error) {
|
if (error) {
|
||||||
Bert.alert(error.reason, 'danger');
|
Bert.alert(error.reason, 'danger');
|
||||||
} else {
|
} else {
|
||||||
|
Meteor.call('users.sendVerificationEmail');
|
||||||
Bert.alert('Welcome!', 'success');
|
Bert.alert('Welcome!', 'success');
|
||||||
history.push('/documents');
|
history.push('/documents');
|
||||||
}
|
}
|
||||||
|
|
|
||||||
42
imports/ui/pages/VerifyEmail/VerifyEmail.js
Normal file
42
imports/ui/pages/VerifyEmail/VerifyEmail.js
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { Alert } from 'react-bootstrap';
|
||||||
|
import { Accounts } from 'meteor/accounts-base';
|
||||||
|
import { Bert } from 'meteor/themeteorchef:bert';
|
||||||
|
|
||||||
|
class VerifyEmail extends React.Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
this.state = { error: null };
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
const { match, history } = this.props;
|
||||||
|
Accounts.verifyEmail(match.params.token, (error) => {
|
||||||
|
if (error) {
|
||||||
|
Bert.alert(error.reason, 'danger');
|
||||||
|
this.setState({ error: `${error.reason}. Please try again.` });
|
||||||
|
} else {
|
||||||
|
setTimeout(() => {
|
||||||
|
Bert.alert('All set, thanks!', 'success');
|
||||||
|
history.push('/documents');
|
||||||
|
}, 2000);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (<div className="VerifyEmail">
|
||||||
|
<Alert bsStyle={!this.state.error ? 'info' : 'danger'}>
|
||||||
|
{!this.state.error ? 'Verifying...' : this.state.error}
|
||||||
|
</Alert>
|
||||||
|
</div>);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
VerifyEmail.propTypes = {
|
||||||
|
match: PropTypes.object.isRequired,
|
||||||
|
history: PropTypes.object.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default VerifyEmail;
|
||||||
Loading…
Add table
Add a link
Reference in a new issue