commit
6b9d557896
6 changed files with 112 additions and 10 deletions
|
|
@ -1,9 +1,13 @@
|
|||
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 rateLimit from '../../../modules/rate-limit';
|
||||
|
||||
Meteor.methods({
|
||||
'users.sendVerificationEmail': function usersResendVerification() {
|
||||
return Accounts.sendVerificationEmail(this.userId);
|
||||
},
|
||||
'users.editProfile': function usersEditProfile(profile) {
|
||||
check(profile, {
|
||||
emailAddress: String,
|
||||
|
|
@ -25,6 +29,7 @@ Meteor.methods({
|
|||
|
||||
rateLimit({
|
||||
methods: [
|
||||
'users.resendVerification',
|
||||
'users.editProfile',
|
||||
],
|
||||
limit: 5,
|
||||
|
|
|
|||
|
|
@ -9,6 +9,18 @@ const emailTemplates = Accounts.emailTemplates;
|
|||
emailTemplates.siteName = name;
|
||||
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 = {
|
||||
subject() {
|
||||
return `[${name}] Reset Your Password`;
|
||||
|
|
@ -16,13 +28,7 @@ emailTemplates.resetPassword = {
|
|||
text(user, url) {
|
||||
const userEmail = user.emails[0].address;
|
||||
const urlWithoutHash = url.replace('#/', '');
|
||||
|
||||
if (Meteor.isDevelopment) console.info(`Reset Password Link: ${urlWithoutHash}`);
|
||||
|
||||
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}.`;
|
||||
if (Meteor.isDevelopment) console.info(`Reset Password Link: ${urlWithoutHash}`); // eslint-disable-line
|
||||
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}.`;
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,10 +1,13 @@
|
|||
/* eslint-disable jsx-a11y/no-href*/
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
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 { createContainer } from 'meteor/react-meteor-data';
|
||||
import { Roles } from 'meteor/alanning:roles';
|
||||
import { Bert } from 'meteor/themeteorchef:bert';
|
||||
import Navigation from '../../components/Navigation/Navigation';
|
||||
import Authenticated from '../../components/Authenticated/Authenticated';
|
||||
import Public from '../../components/Public/Public';
|
||||
|
|
@ -16,6 +19,7 @@ import EditDocument from '../../pages/EditDocument/EditDocument';
|
|||
import Signup from '../../pages/Signup/Signup';
|
||||
import Login from '../../pages/Login/Login';
|
||||
import Logout from '../../pages/Logout/Logout';
|
||||
import VerifyEmail from '../../pages/VerifyEmail/VerifyEmail';
|
||||
import RecoverPassword from '../../pages/RecoverPassword/RecoverPassword';
|
||||
import ResetPassword from '../../pages/ResetPassword/ResetPassword';
|
||||
import Profile from '../../pages/Profile/Profile';
|
||||
|
|
@ -27,9 +31,20 @@ import ExamplePage from '../../pages/ExamplePage/ExamplePage';
|
|||
|
||||
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 => (
|
||||
<Router>
|
||||
{!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} />
|
||||
<Grid>
|
||||
<Switch>
|
||||
|
|
@ -42,6 +57,7 @@ const App = props => (
|
|||
<Public path="/signup" component={Signup} {...props} />
|
||||
<Public path="/login" component={Login} {...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="reset-password" path="/reset-password/:token" component={ResetPassword} />
|
||||
<Route name="terms" path="/terms" component={Terms} />
|
||||
|
|
@ -55,8 +71,16 @@ const App = props => (
|
|||
</Router>
|
||||
);
|
||||
|
||||
App.defaultProps = {
|
||||
userId: '',
|
||||
emailAddress: '',
|
||||
};
|
||||
|
||||
App.propTypes = {
|
||||
loading: PropTypes.bool.isRequired,
|
||||
userId: PropTypes.string,
|
||||
emailAddress: PropTypes.string,
|
||||
emailVerified: PropTypes.bool.isRequired,
|
||||
};
|
||||
|
||||
const getUserName = name => ({
|
||||
|
|
@ -78,5 +102,8 @@ export default createContainer(() => {
|
|||
authenticated: !loggingIn && !!userId,
|
||||
name: name || emailAddress,
|
||||
roles: !loading && Roles.getRolesForUser(userId),
|
||||
userId,
|
||||
emailAddress,
|
||||
emailVerified: user ? user && user.emails && user.emails[0].verified : true,
|
||||
};
|
||||
}, App);
|
||||
|
|
|
|||
|
|
@ -1,3 +1,23 @@
|
|||
@import '../../stylesheets/colors';
|
||||
|
||||
.App > .container {
|
||||
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 PropTypes from 'prop-types';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { Meteor } from 'meteor/meteor';
|
||||
import { Accounts } from 'meteor/accounts-base';
|
||||
import { Bert } from 'meteor/themeteorchef:bert';
|
||||
import OAuthLoginButtons from '../../components/OAuthLoginButtons/OAuthLoginButtons';
|
||||
|
|
@ -71,6 +72,7 @@ class Signup extends React.Component {
|
|||
if (error) {
|
||||
Bert.alert(error.reason, 'danger');
|
||||
} else {
|
||||
Meteor.call('users.sendVerificationEmail');
|
||||
Bert.alert('Welcome!', 'success');
|
||||
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