()
16 | .Build();
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/src/ReactBoilerplate/Properties/launchSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "iisSettings": {
3 | "windowsAuthentication": false,
4 | "anonymousAuthentication": true,
5 | "iisExpress": {
6 | "applicationUrl": "http://localhost:15806/",
7 | "sslPort": 0
8 | }
9 | },
10 | "profiles": {
11 | "Development: IIS Express": {
12 | "commandName": "IISExpress",
13 | "launchBrowser": true,
14 | "environmentVariables": {
15 | "ASPNETCORE_ENVIRONMENT": "Development"
16 | }
17 | },
18 | "Development: .NET CLI": {
19 | "commandName": "Project",
20 | "launchBrowser": true,
21 | "environmentVariables": {
22 | "ASPNETCORE_ENVIRONMENT": "Development"
23 | },
24 | "applicationUrl": "http://localhost:15806/"
25 | },
26 | "Production: Kestrel": {
27 | "commandName": "Kestrel",
28 | "launchBrowser": true,
29 | "environmentVariables": {
30 | "ASPNETCORE_ENVIRONMENT": "Production"
31 | },
32 | "applicationUrl": "http://localhost:15806/"
33 | }
34 | }
35 | }
--------------------------------------------------------------------------------
/src/ReactBoilerplate/React.sublime-project:
--------------------------------------------------------------------------------
1 | {
2 | "folders":
3 | [
4 | {
5 | "path": ".",
6 | "folder_exclude_patterns": ["bower_components", "node_modules"],
7 | "file_exclude_patterns": ["React.xproj*", "project.lock.json"]
8 | }
9 | ]
10 | }
11 |
--------------------------------------------------------------------------------
/src/ReactBoilerplate/ReactBoilerplate.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netcoreapp2.0
5 | true
6 | ReactBoilerplate
7 | Exe
8 | ReactBoilerplate
9 | 89e1d495-c355-4990-bb58-6384b3b2e6df
10 | $(AssetTargetFallback);dotnet5.6;dnxcore50;portable-net45+win8
11 |
12 |
13 |
14 |
15 | PreserveNewest
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/src/ReactBoilerplate/Scripts/client.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import { Router, browserHistory } from 'react-router';
4 | import getRoutes from './routes';
5 | import { Provider } from 'react-redux';
6 | import configureStore from './redux/configureStore';
7 | import { syncHistoryWithStore } from 'react-router-redux';
8 | import ApiClient from './helpers/ApiClient';
9 |
10 | const client = new ApiClient();
11 | const store = configureStore(window.__data, browserHistory, client);
12 | const history = syncHistoryWithStore(browserHistory, store);
13 |
14 | ReactDOM.render(
15 |
16 |
17 | {getRoutes(store)}
18 |
19 | ,
20 | document.getElementById('content')
21 | );
22 |
--------------------------------------------------------------------------------
/src/ReactBoilerplate/Scripts/components/ChangeEmailForm/ChangeEmailForm.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Form from 'components/Form';
3 | import { reduxForm } from 'redux-form';
4 | import { Input } from 'components';
5 | import { changeEmail } from 'redux/modules/manage';
6 |
7 | class ChangeEmailForm extends Form {
8 | constructor(props) {
9 | super(props);
10 | this.success = this.success.bind(this);
11 | this.state = { success: false };
12 | }
13 | success() {
14 | this.setState({ success: true });
15 | }
16 | render() {
17 | const {
18 | fields: { currentPassword, email, emailConfirm }
19 | } = this.props;
20 | const {
21 | success
22 | } = this.state;
23 | return (
24 |
25 | {success &&
26 |
27 | An email has been sent to your email to confirm the change.
28 |
29 | }
30 | {!success &&
31 |
44 | }
45 |
46 | );
47 | }
48 | }
49 |
50 | ChangeEmailForm = reduxForm({
51 | form: 'changeEmail',
52 | fields: ['currentPassword', 'email', 'emailConfirm']
53 | },
54 | (state) => state,
55 | { }
56 | )(ChangeEmailForm);
57 |
58 | export default ChangeEmailForm;
59 |
--------------------------------------------------------------------------------
/src/ReactBoilerplate/Scripts/components/ChangePasswordForm/ChangePasswordForm.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Form from 'components/Form';
3 | import { reduxForm } from 'redux-form';
4 | import { Input } from 'components';
5 | import { changePassword } from 'redux/modules/manage';
6 |
7 | class ChangePasswordForm extends Form {
8 | constructor(props) {
9 | super(props);
10 | this.success = this.success.bind(this);
11 | this.state = { success: false };
12 | }
13 | success() {
14 | this.setState({ success: true });
15 | }
16 | render() {
17 | const {
18 | fields: { oldPassword, newPassword, newPasswordConfirm }
19 | } = this.props;
20 | const {
21 | success
22 | } = this.state;
23 | return (
24 |
25 | {success &&
26 |
27 | Your password has been changed.
28 |
29 | }
30 | {!success &&
31 |
44 | }
45 |
46 | );
47 | }
48 | }
49 |
50 | ChangePasswordForm = reduxForm({
51 | form: 'changePassword',
52 | fields: ['oldPassword', 'newPassword', 'newPasswordConfirm']
53 | },
54 | (state) => state,
55 | { }
56 | )(ChangePasswordForm);
57 |
58 | export default ChangePasswordForm;
59 |
--------------------------------------------------------------------------------
/src/ReactBoilerplate/Scripts/components/ErrorList.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { Glyphicon } from 'react-bootstrap';
3 |
4 | class ErrorList extends Component {
5 | render() {
6 | const {
7 | errors
8 | } = this.props;
9 | if (!errors) return null;
10 | if (Array.isArray(errors)) {
11 | if (errors.length === 0) return null;
12 | return (
13 |
14 | {errors.map((err, i) =>
15 | (
16 |
17 |
18 | {' '}
19 | {err}
20 |
21 | ))}
22 |
23 | );
24 | }
25 | return null;
26 | }
27 | }
28 |
29 | export default ErrorList;
30 |
--------------------------------------------------------------------------------
/src/ReactBoilerplate/Scripts/components/ExternalLogin/ExternalLogin.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { connect } from 'react-redux';
3 | import { authenticate } from 'redux/modules/externalLogin';
4 | import { ExternalLoginButton } from 'components';
5 |
6 | class ExternalLogin extends Component {
7 | constructor(props) {
8 | super(props);
9 | this.loginClick = this.loginClick.bind(this);
10 | }
11 | loginClick(scheme) {
12 | return (event) => {
13 | event.preventDefault();
14 | this.props.authenticate(scheme);
15 | };
16 | }
17 | render() {
18 | const {
19 | loginProviders
20 | } = this.props;
21 | return (
22 |
23 | {loginProviders.map((loginProvider, i) =>
24 | (
25 |
26 |
30 | {' '}
31 |
32 | ))}
33 |
34 | );
35 | }
36 | }
37 |
38 | export default connect(
39 | (state) => ({ loginProviders: state.externalLogin.loginProviders, location: state.routing.locationBeforeTransitions }),
40 | { authenticate }
41 | )(ExternalLogin);
42 |
--------------------------------------------------------------------------------
/src/ReactBoilerplate/Scripts/components/ExternalLoginButton.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Button } from 'react-bootstrap';
3 |
4 | const bootstrapSocial = require('bootstrap-social');
5 | const fontAwesome = require('font-awesome/scss/font-awesome.scss');
6 |
7 | export default (props) =>
8 | (
9 |
12 |
13 | {props.text}
14 |
15 | );
16 |
--------------------------------------------------------------------------------
/src/ReactBoilerplate/Scripts/components/ForgotPasswordForm/ForgotPasswordForm.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Form from 'components/Form';
3 | import { reduxForm } from 'redux-form';
4 | import { Input } from 'components';
5 | import { forgotPassword } from 'redux/modules/account';
6 |
7 | class ForgotPasswordForm extends Form {
8 | constructor(props) {
9 | super(props);
10 | this.success = this.success.bind(this);
11 | this.state = { success: false };
12 | }
13 | success() {
14 | this.setState({ success: true });
15 | }
16 | render() {
17 | const {
18 | fields: { email }
19 | } = this.props;
20 | const {
21 | success
22 | } = this.state;
23 | return (
24 |
25 | {success &&
26 |
27 | Please check your email to reset your password.
28 |
29 | }
30 | {!success &&
31 |
42 | }
43 |
44 | );
45 | }
46 | }
47 |
48 | ForgotPasswordForm = reduxForm({
49 | form: 'forgotPassword',
50 | fields: ['email']
51 | },
52 | (state) => state,
53 | { }
54 | )(ForgotPasswordForm);
55 |
56 | export default ForgotPasswordForm;
57 |
--------------------------------------------------------------------------------
/src/ReactBoilerplate/Scripts/components/Form.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { modelStateErrorToFormFields } from '../utils/modelState';
3 | import { ErrorList } from 'components';
4 |
5 | class Form extends Component {
6 | modifyValues(values) {
7 | return values;
8 | }
9 | handleApiSubmit(action, success, error) {
10 | const {
11 | handleSubmit
12 | } = this.props;
13 | return handleSubmit((values, dispatch) =>
14 | new Promise((resolve, reject) => {
15 | dispatch(action(this.modifyValues(values)))
16 | .then(
17 | (result) => {
18 | if (result.success) {
19 | resolve();
20 | if (success) {
21 | success(result);
22 | }
23 | } else {
24 | reject(modelStateErrorToFormFields(result.errors));
25 | if (error) {
26 | error(result);
27 | }
28 | }
29 | },
30 | (result) => {
31 | reject(modelStateErrorToFormFields(result.errors));
32 | if (error) {
33 | error(result);
34 | }
35 | });
36 | })
37 | );
38 | }
39 | renderGlobalErrorList() {
40 | const {
41 | error
42 | } = this.props;
43 | if (!error) {
44 | return null;
45 | }
46 | if (!error.errors) {
47 | return null;
48 | }
49 | return ( );
50 | }
51 | }
52 |
53 | export default Form;
54 |
--------------------------------------------------------------------------------
/src/ReactBoilerplate/Scripts/components/Input.js:
--------------------------------------------------------------------------------
1 | import React, { Component, PropTypes } from 'react';
2 | import classNames from 'classnames';
3 | import { Glyphicon } from 'react-bootstrap';
4 |
5 | class Input extends Component {
6 | static propTypes = {
7 | field: PropTypes.object.isRequired,
8 | type: React.PropTypes.oneOf([
9 | 'password',
10 | 'text',
11 | 'option',
12 | 'checkbox'
13 | ]),
14 | };
15 | buildFieldProps() {
16 | const {
17 | defaultChecked,
18 | defaultValue,
19 | name,
20 | onBlur,
21 | onChange,
22 | onDragStart,
23 | onDrop,
24 | onFocus
25 | } = this.props.field;
26 | return {
27 | defaultChecked,
28 | defaultValue,
29 | name,
30 | onBlur,
31 | onChange,
32 | onDragStart,
33 | onDrop,
34 | onFocus
35 | };
36 | }
37 | renderErrorList(errors) {
38 | if (!errors) {
39 | return null;
40 | }
41 | return (
42 |
43 | {errors.map((err, i) =>
44 | (
45 |
48 |
49 | {' '}
50 | {err}
51 |
52 | ))}
53 |
54 | );
55 | }
56 | renderInput() {
57 | return (
58 |
65 | );
66 | }
67 | renderOption() {
68 | const {
69 | options
70 | } = this.props;
71 | return (
72 |
78 | {options.map((option, i) =>
79 | (
80 | {option.display}
81 | ))}
82 |
83 | );
84 | }
85 | renderCheckBox() {
86 | return (
87 |
88 |
89 | {this.props.label}
90 |
91 | );
92 | }
93 | render() {
94 | let hasError = false;
95 | let errors;
96 | if (this.props.field.touched && this.props.field.invalid) {
97 | hasError = true;
98 | errors = this.props.field.error.errors;
99 | if (!Array.isArray(errors)) {
100 | console.error('The errors object does not seem to be an array of errors.'); // eslint-disable-line max-len
101 | errors = null;
102 | }
103 | if (errors.length === 0) {
104 | console.error('The errors array is empty. If it is empty, no array should be provided, the field is valid.'); // eslint-disable-line max-len
105 | }
106 | }
107 | const rowClass = classNames({
108 | 'form-group': true,
109 | 'has-error': hasError,
110 | });
111 | let input;
112 | switch (this.props.type) {
113 | case 'password':
114 | case 'text':
115 | input = this.renderInput();
116 | break;
117 | case 'option':
118 | input = this.renderOption();
119 | break;
120 | case 'checkbox':
121 | input = this.renderCheckBox();
122 | break;
123 | default:
124 | throw new Error('unknown type');
125 | }
126 | return (
127 |
128 | {(this.props.type !== 'checkbox') &&
129 |
{this.props.label}
130 | }
131 |
132 | {input}
133 | {this.renderErrorList(errors)}
134 |
135 |
136 | );
137 | }
138 | }
139 |
140 | Input.defaultProps = {
141 | type: 'text'
142 | };
143 |
144 | export default Input;
145 |
--------------------------------------------------------------------------------
/src/ReactBoilerplate/Scripts/components/LoginForm/LoginForm.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Form from 'components/Form';
3 | import { reduxForm } from 'redux-form';
4 | import { Input, ExternalLogin } from 'components';
5 | import { login } from 'redux/modules/account';
6 | import { Row, Col } from 'react-bootstrap';
7 |
8 | class LoginForm extends Form {
9 | render() {
10 | const {
11 | fields: { userName, password },
12 | loginProviders
13 | } = this.props;
14 | return (
15 |
34 | );
35 | }
36 | }
37 |
38 | LoginForm = reduxForm({
39 | form: 'login',
40 | fields: ['userName', 'password', 'rememberMe']
41 | },
42 | (state) => ({ loginProviders: state.externalLogin.loginProviders }),
43 | { }
44 | )(LoginForm);
45 |
46 | export default LoginForm;
47 |
--------------------------------------------------------------------------------
/src/ReactBoilerplate/Scripts/components/RegisterForm/RegisterForm.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Form from 'components/Form';
3 | import { reduxForm } from 'redux-form';
4 | import { Input, ExternalLoginButton, ExternalLogin } from 'components';
5 | import { register } from 'redux/modules/account';
6 | import { clearAuthentication as clearExternalAuthentication } from 'redux/modules/externalLogin';
7 | import { Button, Row, Col } from 'react-bootstrap';
8 |
9 | class RegisterForm extends Form {
10 | modifyValues(values) {
11 | return {
12 | ...values,
13 | linkExternalLogin: this.props.externalLogin.externalAuthenticated
14 | };
15 | }
16 | onRemoveExternalAuthClick(action) {
17 | return (event) => {
18 | event.preventDefault();
19 | action();
20 | };
21 | }
22 | render() {
23 | const {
24 | fields: { userName, email, password, passwordConfirm },
25 | externalLogin: { externalAuthenticated, externalAuthenticatedProvider, loginProviders }
26 | } = this.props;
27 | return (
28 |
64 | );
65 | }
66 | }
67 |
68 | RegisterForm = reduxForm({
69 | form: 'register',
70 | fields: ['userName', 'email', 'password', 'passwordConfirm']
71 | },
72 | (state) => ({
73 | externalLogin: state.externalLogin,
74 | initialValues: { userName: (state.externalLogin.proposedUserName ? state.externalLogin.proposedUserName : ''), email: (state.externalLogin.proposedEmail ? state.externalLogin.proposedEmail : '') }
75 | }),
76 | { clearExternalAuthentication }
77 | )(RegisterForm);
78 |
79 | export default RegisterForm;
80 |
--------------------------------------------------------------------------------
/src/ReactBoilerplate/Scripts/components/ResetPasswordForm/ResetPasswordForm.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Form from 'components/Form';
3 | import { reduxForm } from 'redux-form';
4 | import { Input } from 'components';
5 | import { resetPassword } from 'redux/modules/account';
6 | import { Link } from 'react-router';
7 |
8 | class ResetPasswordForm extends Form {
9 | constructor(props) {
10 | super(props);
11 | this.success = this.success.bind(this);
12 | this.state = { success: false };
13 | }
14 | modifyValues(values) {
15 | return {
16 | ...values,
17 | code: this.props.code
18 | };
19 | }
20 | success() {
21 | this.setState({ success: true });
22 | }
23 | render() {
24 | const {
25 | fields: { email, password, passwordConfirm }
26 | } = this.props;
27 | const {
28 | success
29 | } = this.state;
30 | return (
31 |
32 | {success &&
33 |
34 | Your password has been reset. Please Click here to log in.
35 |
36 | }
37 | {!success &&
38 |
51 | }
52 |
53 | );
54 | }
55 | }
56 |
57 | ResetPasswordForm = reduxForm({
58 | form: 'resetPassword',
59 | fields: ['email', 'password', 'passwordConfirm']
60 | },
61 | (state) => {
62 | let code = null;
63 | if (state.routing.locationBeforeTransitions) {
64 | if (state.routing.locationBeforeTransitions.query) {
65 | code = state.routing.locationBeforeTransitions.query.code;
66 | }
67 | }
68 | return {
69 | code
70 | };
71 | },
72 | { }
73 | )(ResetPasswordForm);
74 |
75 | export default ResetPasswordForm;
76 |
--------------------------------------------------------------------------------
/src/ReactBoilerplate/Scripts/components/Spinner.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | const fontAwesome = require('font-awesome/scss/font-awesome.scss');
4 |
5 | const spinnerClass = fontAwesome.fa + ' ' + fontAwesome['fa-spinner'] + ' ' + fontAwesome['fa-spin'] + ' ' + fontAwesome['fa-5x'];
6 |
7 | export default () =>
8 | (
9 |
10 |
11 |
12 | );
13 |
--------------------------------------------------------------------------------
/src/ReactBoilerplate/Scripts/components/TwoFactor/SendCodeForm.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Form from 'components/Form';
3 | import { reduxForm } from 'redux-form';
4 | import { Input } from 'components';
5 | import { sendCode } from 'redux/modules/account';
6 |
7 | class SendCodeForm extends Form {
8 | render() {
9 | const {
10 | fields: { provider },
11 | userFactors
12 | } = this.props;
13 | return (
14 |
23 | );
24 | }
25 | }
26 |
27 | SendCodeForm = reduxForm({
28 | form: 'sendCode',
29 | fields: ['provider']
30 | },
31 | (state) => ({
32 | userFactors: state.account.userFactors,
33 | initialValues: { provider: (state.account.userFactors.length > 0 ? state.account.userFactors[0] : '') }
34 | }),
35 | { }
36 | )(SendCodeForm);
37 |
38 | export default SendCodeForm;
39 |
--------------------------------------------------------------------------------
/src/ReactBoilerplate/Scripts/components/TwoFactor/TwoFactor.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { connect } from 'react-redux';
3 | import SendCodeForm from './SendCodeForm';
4 | import VerifyCodeForm from './VerifyCodeForm';
5 | import { resetLoginState } from 'redux/modules/account';
6 |
7 | class TwoFactor extends Component {
8 | componentWillUnmount() {
9 | this.props.resetLoginState();
10 | }
11 | render() {
12 | const {
13 | account: { requiresTwoFactor, userFactors, sentCode }
14 | } = this.props;
15 | if (sentCode) {
16 | return (
17 |
18 | );
19 | }
20 | if (requiresTwoFactor) {
21 | return (
22 |
23 | );
24 | }
25 | return (
);
26 | }
27 | }
28 |
29 | export default connect(
30 | (state) => ({ account: state.account }),
31 | { resetLoginState }
32 | )(TwoFactor);
33 |
--------------------------------------------------------------------------------
/src/ReactBoilerplate/Scripts/components/TwoFactor/VerifyCodeForm.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Form from 'components/Form';
3 | import { reduxForm } from 'redux-form';
4 | import { Input } from 'components';
5 | import { verifyCode } from 'redux/modules/account';
6 |
7 | class VerifyCodeForm extends Form {
8 | modifyValues(values) {
9 | return {
10 | ...values,
11 | provider: this.props.sentCodeWithProvider
12 | };
13 | }
14 | render() {
15 | const {
16 | fields: { code, rememberMe, rememberBrowser }
17 | } = this.props;
18 | return (
19 |
30 | );
31 | }
32 | }
33 |
34 | VerifyCodeForm = reduxForm({
35 | form: 'verifyCode',
36 | fields: ['code', 'rememberMe', 'rememberBrowser']
37 | },
38 | (state) => ({
39 | sentCodeWithProvider: state.account.sentCodeWithProvider,
40 | initialValues: {
41 | rememberMe: true,
42 | rememberBrowser: true
43 | }
44 | }),
45 | { }
46 | )(VerifyCodeForm);
47 |
48 | export default VerifyCodeForm;
49 |
--------------------------------------------------------------------------------
/src/ReactBoilerplate/Scripts/components/index.js:
--------------------------------------------------------------------------------
1 | export LoginForm from './LoginForm/LoginForm';
2 | export RegisterForm from './RegisterForm/RegisterForm';
3 | export ForgotPasswordForm from './ForgotPasswordForm/ForgotPasswordForm';
4 | export ResetPasswordForm from './ResetPasswordForm/ResetPasswordForm';
5 | export ChangePasswordForm from './ChangePasswordForm/ChangePasswordForm';
6 | export Input from './Input';
7 | export ExternalLogin from './ExternalLogin/ExternalLogin';
8 | export ExternalLoginButton from './ExternalLoginButton';
9 | export Spinner from './Spinner';
10 | export ErrorList from './ErrorList';
11 | export TwoFactor from './TwoFactor/TwoFactor';
12 | export ChangeEmailForm from './ChangeEmailForm/ChangeEmailForm';
13 | // There is a bug in babel. When exporting types that will be inherited,
14 | // you must import them directly from the component. You can't proxy
15 | // them like this index.js does.
16 | // http://stackoverflow.com/questions/28551582/traceur-runtime-super-expression-must-either-be-null-or-a-function-not-undefin
17 | // export Form from './Form';
18 |
--------------------------------------------------------------------------------
/src/ReactBoilerplate/Scripts/config.js:
--------------------------------------------------------------------------------
1 | export default {
2 | app: {
3 | title: 'ASP.NET React Example',
4 | description: 'This is an example, using React with ASP.NET MVC.',
5 | head: {
6 | titleTemplate: 'React Example: %s',
7 | meta: [
8 | { name: 'description', content: 'This is an example, using React with ASP.NET MVC.' },
9 | ]
10 | }
11 | }
12 | };
13 |
--------------------------------------------------------------------------------
/src/ReactBoilerplate/Scripts/containers/About/About.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import Helmet from 'react-helmet';
3 |
4 | export default class About extends Component {
5 | render() {
6 | return (
7 |
8 |
About us...
9 |
10 |
11 | );
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/src/ReactBoilerplate/Scripts/containers/App/App.js:
--------------------------------------------------------------------------------
1 | import React, { Component, PropTypes } from 'react';
2 | import { connect } from 'react-redux';
3 | import Helmet from 'react-helmet';
4 | import config from '../../config';
5 | import { IndexLink } from 'react-router';
6 | import { LinkContainer, IndexLinkContainer } from 'react-router-bootstrap';
7 | import Navbar from 'react-bootstrap/lib/Navbar';
8 | import Nav from 'react-bootstrap/lib/Nav';
9 | import NavItem from 'react-bootstrap/lib/NavItem';
10 | import { logoff } from '../../redux/modules/account';
11 | import { push } from 'react-router-redux';
12 | import TwoFactorModal from './Modals/TwoFactorModal';
13 |
14 | require('./App.scss');
15 |
16 | class App extends Component {
17 | static propTypes = {
18 | children: PropTypes.object.isRequired
19 | };
20 | constructor(props) {
21 | super(props);
22 | this.logoffClick = this.logoffClick.bind(this);
23 | }
24 | logoffClick() {
25 | this.props.logoff();
26 | this.props.pushState('/');
27 | }
28 | renderLoggedInLinks(user) {
29 | return (
30 |
31 |
32 | Hello {user.userName}!
33 |
34 |
35 | Log off
36 |
37 |
38 | );
39 | }
40 | renderAnonymousLinks() {
41 | return (
42 |
43 |
44 | Register
45 |
46 |
47 | Login
48 |
49 |
50 | );
51 | }
52 | render() {
53 | const {
54 | user
55 | } = this.props;
56 | let loginLinks;
57 | if (user) {
58 | loginLinks = this.renderLoggedInLinks(user);
59 | } else {
60 | loginLinks = this.renderAnonymousLinks();
61 | }
62 | return (
63 |
64 |
65 |
66 |
67 |
68 |
69 | {config.app.title}
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 | Home
78 |
79 |
80 | About
81 |
82 |
83 | Contact
84 |
85 |
86 | {loginLinks}
87 |
88 |
89 |
90 | {this.props.children}
91 |
92 |
95 |
96 |
97 |
98 | );
99 | }
100 | }
101 |
102 | export default connect(
103 | state => ({ user: state.auth.user }),
104 | { logoff, pushState: push }
105 | )(App);
106 |
--------------------------------------------------------------------------------
/src/ReactBoilerplate/Scripts/containers/App/App.scss:
--------------------------------------------------------------------------------
1 | body {
2 | padding-top: 50px;
3 | padding-bottom: 20px;
4 | }
5 |
6 | /* Wrapping element */
7 | /* Set some basic padding to keep content from hitting the edges */
8 | .body-content {
9 | padding-left: 15px;
10 | padding-right: 15px;
11 | }
12 |
13 | /* Set widths on the form inputs since otherwise they're 100% wide */
14 | input,
15 | select,
16 | textarea {
17 | max-width: 280px;
18 | }
19 |
20 | /* Carousel */
21 | .carousel-caption {
22 | z-index: 10 !important;
23 | }
24 |
25 | .carousel-caption p {
26 | font-size: 20px;
27 | line-height: 1.4;
28 | }
29 |
30 | @media (min-width: 768px) {
31 | .carousel-caption {
32 | z-index: 10 !important;
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/src/ReactBoilerplate/Scripts/containers/App/Modals/TwoFactorModal.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { connect } from 'react-redux';
3 | import { Modal, Button } from 'react-bootstrap';
4 | import { resetLoginState } from 'redux/modules/account';
5 | import { TwoFactor } from 'components';
6 |
7 | class TwoFactorModal extends Component {
8 | constructor(props) {
9 | super(props);
10 | this.close = this.close.bind(this);
11 | }
12 | close() {
13 | this.props.resetLoginState();
14 | }
15 | render() {
16 | const {
17 | requiresTwoFactor
18 | } = this.props.account;
19 | return (
20 |
21 |
22 | Security
23 |
24 |
25 |
26 |
27 |
28 | Close
29 |
30 |
31 | );
32 | }
33 | }
34 |
35 | export default connect(
36 | state => ({ account: state.account }),
37 | { resetLoginState }
38 | )(TwoFactorModal);
39 |
--------------------------------------------------------------------------------
/src/ReactBoilerplate/Scripts/containers/ConfirmEmail/ConfirmEmail.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { connect } from 'react-redux';
3 | import { Link } from 'react-router';
4 |
5 | class ConfirmEmail extends Component {
6 | render() {
7 | const {
8 | success,
9 | change // was this a result of a change of an email
10 | } = this.props;
11 | return (
12 |
13 | {!success &&
14 |
15 |
Error.
16 | An error occurred while processing your request.
17 |
18 | }
19 | {success &&
20 |
21 | {change &&
22 |
23 |
Change email
24 |
25 | Your email was succesfully changed.
26 |
27 |
28 | }
29 | {!change &&
30 |
31 |
Confirm email
32 |
33 | Thank you for confirming your email.
34 | Please Click here to Log in.
35 |
36 |
37 | }
38 |
39 | }
40 |
41 | );
42 | }
43 | }
44 |
45 | export default connect(
46 | (state) => ({ success: state.viewBag.success, change: state.viewBag.change }),
47 | { }
48 | )(ConfirmEmail);
49 |
--------------------------------------------------------------------------------
/src/ReactBoilerplate/Scripts/containers/Contact/Contact.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import Helmet from 'react-helmet';
3 |
4 | export default class Contact extends Component {
5 | render() {
6 | return (
7 |
8 |
Contact
9 |
10 |
11 | );
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/src/ReactBoilerplate/Scripts/containers/ForgotPassword/ForgotPassword.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { ForgotPasswordForm } from 'components';
3 | import { connect } from 'react-redux';
4 |
5 | class ForgotPassword extends Component {
6 | render() {
7 | return (
8 |
9 |
Forgot your password?
10 | Enter your email.
11 |
12 |
13 |
14 | );
15 | }
16 | }
17 |
18 | export default connect(
19 | state => ({ user: state.auth.user }),
20 | { }
21 | )(ForgotPassword);
22 |
--------------------------------------------------------------------------------
/src/ReactBoilerplate/Scripts/containers/Home/ASP-NET-Banners-01.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pauldotknopf/react-aspnet-boilerplate/a605a4040d3efb8dc07f5cfda5462d398e6d4c7b/src/ReactBoilerplate/Scripts/containers/Home/ASP-NET-Banners-01.png
--------------------------------------------------------------------------------
/src/ReactBoilerplate/Scripts/containers/Home/ASP-NET-Banners-02.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pauldotknopf/react-aspnet-boilerplate/a605a4040d3efb8dc07f5cfda5462d398e6d4c7b/src/ReactBoilerplate/Scripts/containers/Home/ASP-NET-Banners-02.png
--------------------------------------------------------------------------------
/src/ReactBoilerplate/Scripts/containers/Home/Banner-01-Azure.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pauldotknopf/react-aspnet-boilerplate/a605a4040d3efb8dc07f5cfda5462d398e6d4c7b/src/ReactBoilerplate/Scripts/containers/Home/Banner-01-Azure.png
--------------------------------------------------------------------------------
/src/ReactBoilerplate/Scripts/containers/Home/Banner-02-VS.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pauldotknopf/react-aspnet-boilerplate/a605a4040d3efb8dc07f5cfda5462d398e6d4c7b/src/ReactBoilerplate/Scripts/containers/Home/Banner-02-VS.png
--------------------------------------------------------------------------------
/src/ReactBoilerplate/Scripts/containers/Home/Home.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import Helmet from 'react-helmet';
3 | import { connect } from 'react-redux';
4 | import { Carousel, CarouselItem } from 'react-bootstrap';
5 |
6 | class Home extends Component {
7 | render() {
8 | return (
9 |
10 |
11 |
12 |
13 |
18 |
19 |
20 | Learn how to build ASP.NET apps that can run anywhere.
21 |
22 | Learn More
23 |
24 |
25 |
26 |
27 |
28 |
33 |
34 |
35 | There are powerful new features in Visual Studio for building modern web apps.
36 |
37 | Learn More
38 |
39 |
40 |
41 |
42 |
43 |
48 |
49 |
50 | Bring in libraries from NuGet, Bower, and npm, and automate
51 | tasks using Grunt or Gulp.
52 |
53 | Learn More
54 |
55 |
56 |
57 |
58 |
59 |
64 |
65 |
66 | Learn how Microsoft's Azure cloud platform allows you to build,
67 | deploy, and scale web apps.
68 |
69 | Learn More
70 |
71 |
72 |
73 |
74 |
75 |
76 | );
77 | }
78 | }
79 |
80 | function mapStateToProps(state) {
81 | return state;
82 | }
83 |
84 | function mapDispatchToProps() {
85 | return {};
86 | }
87 |
88 | export default connect(
89 | mapStateToProps,
90 | mapDispatchToProps
91 | )(Home);
92 |
--------------------------------------------------------------------------------
/src/ReactBoilerplate/Scripts/containers/Home/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pauldotknopf/react-aspnet-boilerplate/a605a4040d3efb8dc07f5cfda5462d398e6d4c7b/src/ReactBoilerplate/Scripts/containers/Home/logo.png
--------------------------------------------------------------------------------
/src/ReactBoilerplate/Scripts/containers/Login/Login.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { LoginForm } from 'components';
3 | import { connect } from 'react-redux';
4 | import { push } from 'react-router-redux';
5 | import { Link } from 'react-router';
6 | import { rehydrateLogin } from 'redux/modules/externalLogin';
7 |
8 | class Login extends Component {
9 | componentWillReceiveProps(nextProps) {
10 | // if the user logged in, redirect the user.
11 | if (!this.props.user && nextProps.user) {
12 | if (this.props.location.query.returnUrl) {
13 | this.props.pushState(this.props.location.query.returnUrl);
14 | } else {
15 | this.props.pushState('/');
16 | }
17 | return;
18 | }
19 | // if the user was externally authenticated, but wasn't registered,
20 | // redirect the user to the register.
21 | if (!this.props.externalLogin.externalAuthenticated && nextProps.externalLogin.externalAuthenticated) {
22 | if (nextProps.externalLogin.signInError) {
23 | // The user requires two-factor login or is locked out.
24 | // This means the user is already registered, so no need
25 | // to redirect to register page.
26 | return;
27 | }
28 |
29 | let registerUrl = '/register';
30 | if (this.props.location.query.returnUrl) {
31 | registerUrl += '?returnUrl=' + this.props.location.query.returnUrl;
32 | }
33 | this.props.pushState(registerUrl);
34 | // whenever we navigate to a new page, the external login info is cleared.
35 | // however, when we navigate to the register page, we want to this info
36 | // so that the register page can associte the external login to the new
37 | // account.
38 | // So, every the `pushState` call clears out `externalLogin`, we will
39 | // need to put it back in
40 | this.props.rehydrateLogin(nextProps.externalLogin);
41 | return;
42 | }
43 | }
44 | render() {
45 | return (
46 |
47 |
Login
48 |
49 |
50 |
51 | Register as a new user?
52 |
53 |
54 | Forgot your password?
55 |
56 |
57 | );
58 | }
59 | }
60 |
61 | export default connect(
62 | state => ({ user: state.auth.user, externalLogin: state.externalLogin }),
63 | { pushState: push, rehydrateLogin }
64 | )(Login);
65 |
--------------------------------------------------------------------------------
/src/ReactBoilerplate/Scripts/containers/Manage/ChangePassword/ChangePassword.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { ChangePasswordForm } from 'components';
3 | import { connect } from 'react-redux';
4 |
5 | class ChangePassword extends Component {
6 | render() {
7 | return (
8 |
9 |
Change Password.
10 | Change Password Form
11 |
12 |
13 |
14 | );
15 | }
16 | }
17 |
18 | export default connect(
19 | state => ({ user: state.auth.user }),
20 | { }
21 | )(ChangePassword);
22 |
--------------------------------------------------------------------------------
/src/ReactBoilerplate/Scripts/containers/Manage/Email/Email.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { connect } from 'react-redux';
3 | import { loadEmail, destroyEmail, verifyEmail } from 'redux/modules/manage';
4 | import { ChangeEmailForm, Spinner } from 'components';
5 | import { Alert, Button } from 'react-bootstrap';
6 |
7 | class Email extends Component {
8 | constructor(props) {
9 | super(props);
10 | this.verifyClick = this.verifyClick.bind(this);
11 | this.state = { sendingEmailVerification: false };
12 | }
13 | componentDidMount() {
14 | this.props.loadEmail();
15 | }
16 | componentWillUnmount() {
17 | this.props.destroyEmail();
18 | }
19 | verifyClick(event) {
20 | event.preventDefault();
21 | this.setState({ sendingEmailVerification: true });
22 | this.props.verifyEmail()
23 | .then(() => {
24 | this.setState({ sendingEmailVerification: false });
25 | }, () => {
26 | this.setState({ sendingEmailVerification: false });
27 | });
28 | }
29 | render() {
30 | const {
31 | email,
32 | emailConfirmed
33 | } = this.props.email;
34 | const {
35 | sendingEmailVerification
36 | } = this.state;
37 | console.log(email);
38 | if (typeof email === 'undefined') {
39 | return ( );
40 | }
41 | return (
42 |
43 |
Email
44 |
45 |
46 |
Current email
47 |
50 |
51 |
52 | {!emailConfirmed &&
53 |
54 | Your email is not verified.
55 |
56 |
59 | Verify
60 |
61 |
62 | }
63 |
Change your email
64 |
65 |
66 | );
67 | }
68 | }
69 |
70 | export default connect(
71 | (state) => ({ email: state.manage.email }),
72 | { loadEmail, destroyEmail, verifyEmail }
73 | )(Email);
74 |
--------------------------------------------------------------------------------
/src/ReactBoilerplate/Scripts/containers/Manage/Index/Index.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { connect } from 'react-redux';
3 |
4 | class Index extends Component {
5 | render() {
6 | return (
7 |
8 | );
9 | }
10 | }
11 |
12 | export default connect(
13 | (state) => state,
14 | { }
15 | )(Index);
16 |
--------------------------------------------------------------------------------
/src/ReactBoilerplate/Scripts/containers/Manage/Logins/Logins.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { connect } from 'react-redux';
3 | import { loadExternalLogins, destroyExternalLogins, addExternalLogin, removeExternalLogin } from 'redux/modules/manage';
4 | import { authenticate as externalAuthenticate, clearAuthentication as clearExternalAuthentication } from 'redux/modules/externalLogin';
5 | import { Button } from 'react-bootstrap';
6 | import { ExternalLoginButton, Spinner, ErrorList } from 'components';
7 |
8 | class Logins extends Component {
9 | constructor(props) {
10 | super(props);
11 | this.addButtonClick = this.addButtonClick.bind(this);
12 | this.removeButtonClick = this.removeButtonClick.bind(this);
13 | }
14 | componentDidMount() {
15 | this.props.loadExternalLogins();
16 | }
17 | componentWillUnmount() {
18 | this.props.destroyExternalLogins();
19 | }
20 | addButtonClick(scheme) {
21 | return (event) => {
22 | event.preventDefault();
23 | this.props.externalAuthenticate(scheme, false /* don't auto sign-in */)
24 | .then((result) => {
25 | this.props.clearExternalAuthentication();
26 | if (result.externalAuthenticated) {
27 | // the user succesfully authenticated with the service.
28 | // add the login to this account.
29 | this.props.addExternalLogin();
30 | }
31 | }, () => {
32 | this.props.clearExternalAuthentication();
33 | });
34 | };
35 | }
36 | removeButtonClick(scheme) {
37 | return (event) => {
38 | event.preventDefault();
39 | this.props.removeExternalLogin(scheme);
40 | };
41 | }
42 | render() {
43 | if (this.props.externalLogins.loading) {
44 | return ( );
45 | }
46 | if (!this.props.externalLogins.currentLogins) {
47 | return ( );
48 | }
49 | const {
50 | currentLogins,
51 | otherLogins,
52 | errors
53 | } = this.props.externalLogins;
54 | return (
55 |
56 |
Manage your external logins
57 |
58 | {(currentLogins.length > 0) &&
59 |
60 |
Current logins
61 |
62 |
63 | {currentLogins.map((currentLogin, i) =>
64 | (
65 |
66 |
67 |
68 | Remove
69 |
70 | {' '}
71 |
75 |
76 |
77 | ))}
78 |
79 |
80 |
81 | }
82 | {(otherLogins.length > 0) &&
83 |
84 |
Add another service to log in.
85 |
86 |
87 | {otherLogins.map((otherLogin, i) =>
88 | (
89 |
90 |
91 |
92 | Add
93 |
94 | {' '}
95 |
99 |
100 |
101 | ))}
102 |
103 |
104 |
105 | }
106 |
107 | );
108 | }
109 | }
110 |
111 | export default connect(
112 | state => ({ externalLogins: state.manage.externalLogins }),
113 | { loadExternalLogins, destroyExternalLogins, externalAuthenticate, clearExternalAuthentication, addExternalLogin, removeExternalLogin }
114 | )(Logins);
115 |
--------------------------------------------------------------------------------
/src/ReactBoilerplate/Scripts/containers/Manage/Manage.js:
--------------------------------------------------------------------------------
1 | import React, { Component, PropTypes } from 'react';
2 | import { connect } from 'react-redux';
3 | import { Row, Col, Nav, NavItem } from 'react-bootstrap';
4 | import { LinkContainer } from 'react-router-bootstrap';
5 |
6 | class Manage extends Component {
7 | static propTypes = {
8 | children: PropTypes.object.isRequired
9 | };
10 | render() {
11 | return (
12 |
13 |
14 |
15 |
16 | Security
17 |
18 |
19 | Email
20 |
21 |
22 | Change password
23 |
24 |
25 | Manage logins
26 |
27 |
28 |
29 |
30 | {this.props.children}
31 |
32 |
33 | );
34 | }
35 | }
36 |
37 | export default connect(
38 | state => ({ user: state.auth.user }),
39 | { }
40 | )(Manage);
41 |
--------------------------------------------------------------------------------
/src/ReactBoilerplate/Scripts/containers/Manage/Security/Security.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { connect } from 'react-redux';
3 | import { loadSecurity, destroySecurity, setTwoFactor } from 'redux/modules/manage';
4 | import { Spinner } from 'components';
5 | import { Alert, Button } from 'react-bootstrap';
6 |
7 | class Security extends Component {
8 | constructor(props) {
9 | super(props);
10 | this.toggleTwoFactorClick = this.toggleTwoFactorClick.bind(this);
11 | }
12 | componentDidMount() {
13 | this.props.loadSecurity();
14 | }
15 | componentWillUnmount() {
16 | this.props.destroySecurity();
17 | }
18 | toggleTwoFactorClick(event) {
19 | event.preventDefault();
20 | const {
21 | twoFactorEnabled
22 | } = this.props.security;
23 | this.props.setTwoFactor(!twoFactorEnabled);
24 | }
25 | render() {
26 | const {
27 | twoFactorEnabled,
28 | validTwoFactorProviders,
29 | settingTwoFactor
30 | } = this.props.security;
31 | if (typeof twoFactorEnabled === 'undefined') {
32 | return ( );
33 | }
34 | return (
35 |
36 |
37 | Two-factor authentication is {twoFactorEnabled ? 'enabled' : 'disabled'} .
38 |
39 |
42 | {twoFactorEnabled ? 'Disable' : 'Enable'}
43 |
44 |
45 | {(twoFactorEnabled && validTwoFactorProviders.length === 0) &&
46 |
47 | Although you have two-factor authentication enabled, you have no valid providers to authenticate with.
48 |
49 | }
50 |
51 | );
52 | }
53 | }
54 |
55 | export default connect(
56 | (state) => ({ security: state.manage.security }),
57 | { loadSecurity, destroySecurity, setTwoFactor }
58 | )(Security);
59 |
--------------------------------------------------------------------------------
/src/ReactBoilerplate/Scripts/containers/NotFound/NotFound.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | export default function NotFound() {
4 | return (
5 |
6 |
Doh! 404!
7 |
These are not the droids you are looking for!
8 |
9 | );
10 | }
11 |
--------------------------------------------------------------------------------
/src/ReactBoilerplate/Scripts/containers/Register/Register.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { RegisterForm } from 'components';
3 | import { connect } from 'react-redux';
4 | import { push } from 'react-router-redux';
5 |
6 | class Register extends Component {
7 | componentWillReceiveProps(nextProps) {
8 | if (!this.props.user && nextProps.user) {
9 | this.props.pushState('/');
10 | }
11 | }
12 | render() {
13 | return (
14 |
15 |
Register
16 | Create a new account.
17 |
18 |
19 |
20 | );
21 | }
22 | }
23 |
24 | export default connect(
25 | state => ({ user: state.auth.user, externalLogin: state.externalLogin }),
26 | { pushState: push }
27 | )(Register);
28 |
--------------------------------------------------------------------------------
/src/ReactBoilerplate/Scripts/containers/ResetPassword/ResetPassword.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { ResetPasswordForm } from 'components';
3 | import { connect } from 'react-redux';
4 |
5 | class ResetPassword extends Component {
6 | render() {
7 | return (
8 |
9 |
Reset password
10 | Reset your password.
11 |
12 |
13 |
14 | );
15 | }
16 | }
17 |
18 | export default connect(
19 | state => ({ user: state.auth.user }),
20 | { }
21 | )(ResetPassword);
22 |
--------------------------------------------------------------------------------
/src/ReactBoilerplate/Scripts/containers/SignIn/SignIn.js:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pauldotknopf/react-aspnet-boilerplate/a605a4040d3efb8dc07f5cfda5462d398e6d4c7b/src/ReactBoilerplate/Scripts/containers/SignIn/SignIn.js
--------------------------------------------------------------------------------
/src/ReactBoilerplate/Scripts/containers/index.js:
--------------------------------------------------------------------------------
1 | export App from './App/App';
2 | export Home from './Home/Home';
3 | export About from './About/About';
4 | export Contact from './Contact/Contact';
5 | export NotFound from './NotFound/NotFound';
6 | export Register from './Register/Register';
7 | export Login from './Login/Login';
8 | export ForgotPassword from './ForgotPassword/ForgotPassword';
9 | export ResetPassword from './ResetPassword/ResetPassword';
10 | export ConfirmEmail from './ConfirmEmail/ConfirmEmail';
11 | export Manage from './Manage/Manage';
12 | export ManageIndex from './Manage/Index/Index';
13 | export ManageSecurity from './Manage/Security/Security';
14 | export ManageChangePassword from './Manage/ChangePassword/ChangePassword';
15 | export ManageLogins from './Manage/Logins/Logins';
16 | export ManageEmail from './Manage/Email/Email';
17 |
--------------------------------------------------------------------------------
/src/ReactBoilerplate/Scripts/helpers/ApiClient.js:
--------------------------------------------------------------------------------
1 | import superagent from 'superagent';
2 |
3 | const methods = ['get', 'post', 'put', 'patch', 'del'];
4 |
5 | class _ApiClient {
6 | constructor(req) {
7 | methods.forEach((method) => {
8 | this[method] = (path, { params, data } = {}) => new Promise((resolve, reject) => {
9 | const request = superagent[method](path);
10 |
11 | if (params) {
12 | request.query(params);
13 | }
14 |
15 | if (__SERVER__ && req.get('cookie')) {
16 | request.set('cookie', req.get('cookie'));
17 | }
18 |
19 | if (data) {
20 | request.send(data);
21 | }
22 |
23 | request.end((err, { body } = {}) => (err ? reject(body || err) : resolve(body)));
24 | });
25 | });
26 | }
27 | }
28 |
29 | const ApiClient = _ApiClient;
30 |
31 | export default ApiClient;
32 |
--------------------------------------------------------------------------------
/src/ReactBoilerplate/Scripts/helpers/Html.js:
--------------------------------------------------------------------------------
1 | import React, { Component, PropTypes } from 'react';
2 | import ReactDOM from 'react-dom/server';
3 | import Helmet from 'react-helmet';
4 | import serialize from 'serialize-javascript';
5 |
6 | export default class Html extends Component {
7 | static propTypes = {
8 | component: PropTypes.node,
9 | store: PropTypes.object
10 | };
11 |
12 | render() {
13 | const { component, store } = this.props;
14 | const content = component ? ReactDOM.renderToString(component) : '';
15 | const head = Helmet.rewind();
16 |
17 | return (
18 |
19 |
20 | {head.base.toComponent()}
21 | {head.title.toComponent()}
22 | {head.meta.toComponent()}
23 | {head.link.toComponent()}
24 | {head.script.toComponent()}
25 |
26 |
27 |
34 |
35 |
36 |
37 |
41 |
42 |
43 |
44 | );
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/src/ReactBoilerplate/Scripts/redux/configureStore.js:
--------------------------------------------------------------------------------
1 | import { createStore, applyMiddleware, compose } from 'redux';
2 | import thunk from 'redux-thunk';
3 | import reducer from './reducer';
4 | import { routerMiddleware } from 'react-router-redux';
5 | import createMiddleware from './middleware/clientMiddleware';
6 |
7 | let devTools = f => f;
8 | if (typeof window === 'object'
9 | && typeof window.devToolsExtension !== 'undefined') {
10 | devTools = window.devToolsExtension();
11 | }
12 |
13 | export default function configureStore(initialState, history, client) {
14 | const enhancer = compose(
15 | applyMiddleware(thunk),
16 | applyMiddleware(routerMiddleware(history)),
17 | applyMiddleware(createMiddleware(client)),
18 | devTools
19 | )(createStore);
20 | return enhancer(reducer, initialState);
21 | }
22 |
--------------------------------------------------------------------------------
/src/ReactBoilerplate/Scripts/redux/middleware/clientMiddleware.js:
--------------------------------------------------------------------------------
1 | export default function clientMiddleware(client) {
2 | return ({ dispatch, getState }) =>
3 | next => action => {
4 | if (typeof action === 'function') {
5 | return action(dispatch, getState);
6 | }
7 |
8 | const { promise, types, ...rest } = action; // eslint-disable-line no-use-before-define
9 | if (!promise) {
10 | return next(action);
11 | }
12 |
13 | const [REQUEST, SUCCESS, FAILURE] = types;
14 | next({ ...rest, type: REQUEST });
15 |
16 | const actionPromise = promise(client);
17 | actionPromise.then(
18 | (result) => next({ ...rest, result, type: SUCCESS }),
19 | (error) => next({ ...rest, error, type: FAILURE })
20 | ).catch((error) => {
21 | console.error('MIDDLEWARE ERROR:', error);
22 | console.error('MIDDLEWARE ERROR:', error.stack);
23 | next({ ...rest, error, type: FAILURE });
24 | });
25 |
26 | return actionPromise;
27 | };
28 | }
29 |
--------------------------------------------------------------------------------
/src/ReactBoilerplate/Scripts/redux/modules/account.js:
--------------------------------------------------------------------------------
1 | export const REGISTER_START = 'react/account/REGISTER_START';
2 | export const REGISTER_COMPLETE = 'react/account/REGISTER_COMPLETE';
3 | export const REGISTER_ERROR = 'react/account/REGISTER_ERROR';
4 |
5 | export const LOGIN_START = 'react/account/LOGIN_START';
6 | export const LOGIN_COMPLETE = 'react/account/LOGIN_COMPLETE';
7 | export const LOGIN_ERROR = 'react/account/LOGIN_ERROR';
8 |
9 | export const LOGOFF_START = 'react/account/LOGOFF_START';
10 | export const LOGOFF_COMPLETE = 'react/account/LOGOFF_COMPLETE';
11 | export const LOGOFF_ERROR = 'react/account/LOGOFF_ERROR';
12 |
13 | export const FORGOTPASSWORD_START = 'react/account/FORGOTPASSWORD_START';
14 | export const FORGOTPASSWORD_COMPLETE = 'react/account/FORGOTPASSWORD_COMPLETE';
15 | export const FORGOTPASSWORD_ERROR = 'react/account/FORGOTPASSWORD_ERROR';
16 |
17 | export const RESETPASSWORD_START = 'react/account/RESETPASSWORD_START';
18 | export const RESETPASSWORD_COMPLETE = 'react/account/RESETPASSWORD_COMPLETE';
19 | export const RESETPASSWORD_ERROR = 'react/account/RESETPASSWORD_ERROR';
20 |
21 | export const SENDCODE_START = 'react/account/SENDCODE_START';
22 | export const SENDCODE_COMPLETE = 'react/account/SENDCODE_COMPLETE';
23 | export const SENDCODE_ERROR = 'react/account/SENDCODE_ERROR';
24 |
25 | export const VERIFYCODE_START = 'react/account/VERIFYCODE_START';
26 | export const VERIFYCODE_COMPLETE = 'react/account/VERIFYCODE_COMPLETE';
27 | export const VERIFYCODE_ERROR = 'react/account/VERIFYCODE_ERROR';
28 |
29 | export const LOGINSTATE_RESET = 'react/account/LOGINSTATE_RESET';
30 |
31 | import { EXTERNALAUTHENTICATE_COMPLETE } from 'redux/modules/externalLogin';
32 |
33 | const initialState = {
34 | sentCode: false,
35 | sentCodeWithProvider: null,
36 | userFactors: null,
37 | requiresTwoFactor: false
38 | };
39 |
40 | export default function reducer(state = initialState, action = {}) {
41 | switch (action.type) {
42 | case LOGINSTATE_RESET:
43 | return initialState;
44 | case LOGIN_COMPLETE:
45 | return {
46 | ...state,
47 | userFactors: action.result.userFactors,
48 | requiresTwoFactor: action.result.requiresTwoFactor
49 | };
50 | case SENDCODE_COMPLETE:
51 | return {
52 | ...state,
53 | sentCode: action.result.success,
54 | sentCodeWithProvider: action.result.provider
55 | };
56 | case EXTERNALAUTHENTICATE_COMPLETE:
57 | if (action.result.requiresTwoFactor) {
58 | return {
59 | ...state,
60 | userFactors: action.result.userFactors,
61 | requiresTwoFactor: true
62 | };
63 | }
64 | return state;
65 | case VERIFYCODE_COMPLETE:
66 | if (action.result.success) {
67 | return initialState; // we logged the user in, reset all the two-factor stuff
68 | }
69 | return state;
70 | default:
71 | return state;
72 | }
73 | }
74 |
75 | export function register(body) {
76 | return {
77 | types: [REGISTER_START, REGISTER_COMPLETE, REGISTER_ERROR],
78 | promise: (client) => client.post('/api/account/register', { data: body })
79 | };
80 | }
81 |
82 | export function login(body) {
83 | return {
84 | types: [LOGIN_START, LOGIN_COMPLETE, LOGIN_ERROR],
85 | promise: (client) => client.post('/api/account/login', { data: body })
86 | };
87 | }
88 |
89 | export function logoff() {
90 | return {
91 | types: [LOGOFF_START, LOGOFF_COMPLETE, LOGOFF_ERROR],
92 | promise: (client) => client.post('/api/account/logoff')
93 | };
94 | }
95 |
96 | export function forgotPassword(body) {
97 | return {
98 | types: [FORGOTPASSWORD_START, FORGOTPASSWORD_COMPLETE, FORGOTPASSWORD_ERROR],
99 | promise: (client) => client.post('/api/account/forgotpassword', { data: body })
100 | };
101 | }
102 |
103 | export function resetPassword(body) {
104 | return {
105 | types: [RESETPASSWORD_START, RESETPASSWORD_COMPLETE, RESETPASSWORD_ERROR],
106 | promise: (client) => client.post('/api/account/resetpassword', { data: body })
107 | };
108 | }
109 |
110 | export function sendCode(body) {
111 | return {
112 | types: [SENDCODE_START, SENDCODE_COMPLETE, SENDCODE_ERROR],
113 | promise: (client) => client.post('/api/account/sendcode', { data: body })
114 | };
115 | }
116 |
117 | export function verifyCode(body) {
118 | return {
119 | types: [VERIFYCODE_START, VERIFYCODE_COMPLETE, VERIFYCODE_ERROR],
120 | promise: (client) => client.post('/api/account/verifycode', { data: body })
121 | };
122 | }
123 |
124 | export function resetLoginState() {
125 | return {
126 | type: LOGINSTATE_RESET
127 | };
128 | }
129 |
--------------------------------------------------------------------------------
/src/ReactBoilerplate/Scripts/redux/modules/auth.js:
--------------------------------------------------------------------------------
1 | import { REGISTER_COMPLETE, LOGIN_COMPLETE, LOGOFF_COMPLETE, VERIFYCODE_COMPLETE } from './account';
2 | import { EXTERNALAUTHENTICATE_COMPLETE } from './externalLogin';
3 |
4 | const initialState = {
5 | loggedIn: false,
6 | user: null
7 | };
8 |
9 | export default function reducer(state = initialState, action = {}) {
10 | switch (action.type) {
11 | case REGISTER_COMPLETE:
12 | case LOGIN_COMPLETE:
13 | if (!action.result.success) {
14 | return state;
15 | }
16 | return {
17 | ...state,
18 | user: action.result.user,
19 | loggedIn: true
20 | };
21 | case LOGOFF_COMPLETE:
22 | if (!action.result.success) {
23 | return state;
24 | }
25 | return {
26 | ...state,
27 | user: null,
28 | loggedIn: false
29 | };
30 | case EXTERNALAUTHENTICATE_COMPLETE:
31 | if (action.result.signedIn) {
32 | return {
33 | ...state,
34 | user: action.result.user,
35 | loggedIn: true
36 | };
37 | }
38 | return state;
39 | case VERIFYCODE_COMPLETE:
40 | if (action.result.success) {
41 | return {
42 | ...state,
43 | user: action.result.user,
44 | loggedIn: true
45 | };
46 | }
47 | return state;
48 | default:
49 | return state;
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/src/ReactBoilerplate/Scripts/redux/modules/externalLogin.js:
--------------------------------------------------------------------------------
1 | import promiseWindow from 'promise-window';
2 | import { LOGOFF_COMPLETE, LOGINSTATE_RESET } from 'redux/modules/account';
3 | import { LOCATION_CHANGE } from 'react-router-redux';
4 |
5 | export const EXTERNALAUTHENTICATE_START = 'react/externalLogin/EXTERNALAUTHENTICATE_START';
6 | export const EXTERNALAUTHENTICATE_COMPLETE = 'react/externalLogin/EXTERNALAUTHENTICATE_COMPLETE';
7 | export const EXTERNALAUTHENTICATE_ERROR = 'react/externalLogin/EXTERNALAUTHENTICATE_ERROR';
8 |
9 | export const EXTERNALAUTHENTICATE_CLEAR = 'react/externalLogin/EXTERNALAUTHENTICATE_CLEAR';
10 | export const EXTERNALAUTHENTICATE_REHYDATE = 'react/externalLogin/EXTERNALAUTHENTICATE_REHYDATE';
11 |
12 | function popupWindowSize(provider) {
13 | switch (provider.toLowerCase()) {
14 | case 'facebook':
15 | return { width: 580, height: 400 };
16 | case 'google':
17 | return { width: 452, height: 633 };
18 | case 'github':
19 | return { width: 1020, height: 618 };
20 | case 'linkedin':
21 | return { width: 527, height: 582 };
22 | case 'twitter':
23 | return { width: 495, height: 645 };
24 | case 'live':
25 | return { width: 500, height: 560 };
26 | case 'yahoo':
27 | return { width: 559, height: 519 };
28 | default:
29 | return { width: 1020, height: 618 };
30 | }
31 | }
32 |
33 | const initialState = {
34 | loginProviders: [], // it is up to the server to provide these values
35 | externalAuthenticated: false,
36 | externalAuthenticatedProvider: null,
37 | signInError: false,
38 | proposedEmail: '',
39 | proposedUserName: ''
40 | };
41 |
42 | export default function reducer(state = initialState, action = {}) {
43 | switch (action.type) {
44 | case EXTERNALAUTHENTICATE_REHYDATE:
45 | return {
46 | ...state,
47 | ...action.result
48 | };
49 | case EXTERNALAUTHENTICATE_COMPLETE:
50 | if (action.result.signInError) {
51 | // We have a valid account with this external login,
52 | // but we couldn't login for some reason.
53 | // We don't want to store this login though, because
54 | // either a two-factor modal will popup will show,
55 | // or lockout message will show to the user.
56 | // Either way, there is no reason to store this external
57 | // login. We can't register with it, or login with it.
58 | return {
59 | ...state,
60 | externalAuthenticated: false,
61 | externalAuthenticatedProvider: null,
62 | signInError: false,
63 | proposedEmail: '',
64 | proposedUserName: ''
65 | };
66 | }
67 | return {
68 | ...state,
69 | externalAuthenticated: action.result.externalAuthenticated,
70 | externalAuthenticatedProvider: action.result.loginProvider,
71 | signInError: action.result.signInError,
72 | proposedEmail: action.result.proposedEmail,
73 | proposedUserName: action.result.proposedUserName
74 | };
75 | case EXTERNALAUTHENTICATE_CLEAR: // when some requests to clear any previously stored external authentications
76 | case LOGOFF_COMPLETE: // when a user logs off
77 | case LOCATION_CHANGE: // when the user navigates to different pages, we want to clear the user's logged in provider.
78 | case LOGINSTATE_RESET:
79 | // let's clear out the any previously stored external authentications that were done on the client.
80 | return {
81 | ...state,
82 | externalAuthenticated: false,
83 | externalAuthenticatedProvider: null,
84 | signInError: false,
85 | proposedEmail: '',
86 | proposedUserName: ''
87 | };
88 | default:
89 | return state;
90 | }
91 | }
92 |
93 | export function authenticate(provider, autoLogin = true) {
94 | return {
95 | types: [EXTERNALAUTHENTICATE_START, EXTERNALAUTHENTICATE_COMPLETE, EXTERNALAUTHENTICATE_ERROR],
96 | promise: () => new Promise((result, reject) => {
97 | const windowSize = popupWindowSize(provider);
98 | promiseWindow.open('/externalloginredirect?provider=' + provider + '&autoLogin=' + autoLogin, { ...windowSize })
99 | .then((windowResult) => {
100 | result(windowResult);
101 | }, () => {
102 | reject({});
103 | });
104 | })
105 | };
106 | }
107 |
108 | export function rehydrateLogin(login) {
109 | return {
110 | type: EXTERNALAUTHENTICATE_REHYDATE,
111 | result: login
112 | };
113 | }
114 |
115 | export function clearAuthentication() {
116 | return {
117 | type: EXTERNALAUTHENTICATE_CLEAR
118 | };
119 | }
120 |
--------------------------------------------------------------------------------
/src/ReactBoilerplate/Scripts/redux/modules/manage/changePassword.js:
--------------------------------------------------------------------------------
1 | export const CHANGEPASSWORD_START = 'react/manage/CHANGEPASSWORD_START';
2 | export const CHANGEPASSWORD_COMPLETE = 'react/manage/CHANGEPASSWORD_COMPLETE';
3 | export const CHANGEPASSWORD_ERROR = 'react/manage/CHANGEPASSWORD_ERROR';
4 |
5 | export function changePassword(body) {
6 | return {
7 | types: [CHANGEPASSWORD_START, CHANGEPASSWORD_COMPLETE, CHANGEPASSWORD_ERROR],
8 | promise: (client) => client.post('/api/manage/changepassword', { data: body })
9 | };
10 | }
11 |
--------------------------------------------------------------------------------
/src/ReactBoilerplate/Scripts/redux/modules/manage/email.js:
--------------------------------------------------------------------------------
1 | export const LOADEMAIL_START = 'react/manage/LOADEMAIL_START';
2 | export const LOADEMAIL_COMPLETE = 'react/manage/LOADEMAIL_COMPLETE';
3 | export const LOADEMAIL_ERROR = 'react/manage/LOADEMAIL_ERROR';
4 |
5 | export const CHANGEEMAIL_START = 'react/manage/CHANGEEMAIL_START';
6 | export const CHANGEEMAIL_COMPLETE = 'react/manage/CHANGEEMAIL_COMPLETE';
7 | export const CHANGEEMAIL_ERROR = 'react/manage/CHANGEEMAIL_ERROR';
8 |
9 | export const VERIFYEMAIL_START = 'react/manage/VERIFYEMAIL_START';
10 | export const VERIFYEMAIL_COMPLETE = 'react/manage/VERIFYEMAIL_COMPLETE';
11 | export const VERIFYEMAIL_ERROR = 'react/manage/VERIFYEMAIL_ERROR';
12 |
13 | export const EMAIL_DESTROY = 'react/manage/EMAIL_DESTROY';
14 |
15 | const initialState = {
16 |
17 | };
18 |
19 | export default function (state = initialState, action = {}) {
20 | switch (action.type) {
21 | case LOADEMAIL_COMPLETE:
22 | return {
23 | ...state,
24 | ...action.result
25 | };
26 | case EMAIL_DESTROY:
27 | return initialState;
28 | default:
29 | return state;
30 | }
31 | }
32 |
33 | export function loadEmail() {
34 | return {
35 | types: [LOADEMAIL_START, LOADEMAIL_COMPLETE, LOADEMAIL_ERROR],
36 | promise: (client) => client.post('/api/manage/email')
37 | };
38 | }
39 |
40 | export function destroyEmail() {
41 | return { type: EMAIL_DESTROY };
42 | }
43 |
44 | export function changeEmail(body) {
45 | return {
46 | types: [CHANGEEMAIL_START, CHANGEEMAIL_COMPLETE, CHANGEEMAIL_ERROR],
47 | promise: (client) => client.post('/api/manage/changeemail', { data: body })
48 | };
49 | }
50 |
51 | export function verifyEmail(body) {
52 | return {
53 | types: [VERIFYEMAIL_START, VERIFYEMAIL_COMPLETE, VERIFYEMAIL_ERROR],
54 | promise: (client) => client.post('/api/manage/verifyemail', { data: body })
55 | };
56 | }
57 |
--------------------------------------------------------------------------------
/src/ReactBoilerplate/Scripts/redux/modules/manage/externalLogins.js:
--------------------------------------------------------------------------------
1 | export const LOADEXTERNALLOGINS_START = 'react/manage/LOADEXTERNALLOGINS_START';
2 | export const LOADEXTERNALLOGINS_COMPLETE = 'react/manage/LOADEXTERNALLOGINS_COMPLETE';
3 | export const LOADEXTERNALLOGINS_ERROR = 'react/manage/LOADEXTERNALLOGINS_ERROR';
4 |
5 | export const ADDEXTERNALLOGIN_START = 'react/manage/ADDEXTERNALLOGIN_START';
6 | export const ADDEXTERNALLOGIN_COMPLETE = 'react/manage/ADDEXTERNALLOGIN_COMPLETE';
7 | export const ADDEXTERNALLOGIN_ERROR = 'react/manage/ADDEXTERNALLOGIN_ERROR';
8 |
9 | export const REMOVEEXTERNALLOGIN_START = 'react/manage/REMOVEEXTERNALLOGIN_START';
10 | export const REMOVEEXTERNALLOGIN_COMPLETE = 'react/manage/REMOVEEXTERNALLOGIN_COMPLETE';
11 | export const REMOVEEXTERNALLOGIN_ERROR = 'react/manage/REMOVEEXTERNALLOGIN_ERROR';
12 |
13 | export const EXTERNALLOGINS_DESTROY = 'react/manage/EXTERNALLOGINS_DESTROY';
14 |
15 | export default function (state = {}, action = {}) {
16 | switch (action.type) {
17 | case LOADEXTERNALLOGINS_START:
18 | return {
19 | ...state,
20 | loading: true
21 | };
22 | case LOADEXTERNALLOGINS_COMPLETE:
23 | case ADDEXTERNALLOGIN_COMPLETE:
24 | case REMOVEEXTERNALLOGIN_COMPLETE:
25 | return {
26 | ...state,
27 | loading: false,
28 | ...action.result.externalLogins,
29 | errors: action.result.errors
30 | };
31 | case LOADEXTERNALLOGINS_ERROR:
32 | return {
33 | ...state,
34 | loading: false,
35 | ...action.result.externalLogins
36 | };
37 | case EXTERNALLOGINS_DESTROY:
38 | return {};
39 | default:
40 | return state;
41 | }
42 | }
43 |
44 | export function loadExternalLogins() {
45 | return {
46 | types: [LOADEXTERNALLOGINS_START, LOADEXTERNALLOGINS_COMPLETE, LOADEXTERNALLOGINS_ERROR],
47 | promise: (client) => client.post('/api/manage/externallogins')
48 | };
49 | }
50 |
51 | export function destroyExternalLogins() {
52 | return { type: EXTERNALLOGINS_DESTROY };
53 | }
54 |
55 | export function addExternalLogin() {
56 | return {
57 | types: [ADDEXTERNALLOGIN_START, ADDEXTERNALLOGIN_COMPLETE, ADDEXTERNALLOGIN_ERROR],
58 | promise: (client) => client.post('/api/manage/addexternallogin')
59 | };
60 | }
61 |
62 | export function removeExternalLogin(body) {
63 | return {
64 | types: [REMOVEEXTERNALLOGIN_START, REMOVEEXTERNALLOGIN_COMPLETE, REMOVEEXTERNALLOGIN_ERROR],
65 | promise: (client) => client.post('/api/manage/removeexternallogin', { data: body })
66 | };
67 | }
68 |
--------------------------------------------------------------------------------
/src/ReactBoilerplate/Scripts/redux/modules/manage/index.js:
--------------------------------------------------------------------------------
1 | import { combineReducers } from 'redux';
2 | import externalLogins from './externalLogins';
3 | import security from './security';
4 | import email from './email';
5 |
6 | export default combineReducers({
7 | externalLogins,
8 | security,
9 | email
10 | });
11 |
12 | export * from './externalLogins';
13 | export * from './changePassword';
14 | export * from './security';
15 | export * from './email';
16 |
--------------------------------------------------------------------------------
/src/ReactBoilerplate/Scripts/redux/modules/manage/security.js:
--------------------------------------------------------------------------------
1 | export const LOADSECURITY_START = 'react/manage/LOADSECURITY_START';
2 | export const LOADSECURITY_COMPLETE = 'react/manage/LOADSECURITY_COMPLETE';
3 | export const LOADSECURITY_ERROR = 'react/manage/LOADSECURITY_ERROR';
4 |
5 | export const SETTWOFACTOR_START = 'react/manage/SETTWOFACTOR_START';
6 | export const SETTWOFACTOR_COMPLETE = 'react/manage/SETTWOFACTOR_COMPLETE';
7 | export const SETTWOFACTOR_ERROR = 'react/manage/SETTWOFACTOR_ERROR';
8 |
9 | export const SECURITY_DESTROY = 'react/manage/SECURITY_DESTROY';
10 |
11 | const initialState = {
12 |
13 | };
14 |
15 | export default function (state = initialState, action = {}) {
16 | switch (action.type) {
17 | case LOADSECURITY_COMPLETE:
18 | return {
19 | ...state,
20 | ...action.result
21 | };
22 | case SETTWOFACTOR_START:
23 | return {
24 | ...state,
25 | settingTwoFactor: true
26 | };
27 | case SETTWOFACTOR_COMPLETE:
28 | return {
29 | ...state,
30 | twoFactorEnabled: action.result.twoFactorEnabled,
31 | settingTwoFactor: false
32 | };
33 | case SETTWOFACTOR_ERROR:
34 | return {
35 | ...state,
36 | settingTwoFactor: false
37 | };
38 | case SECURITY_DESTROY:
39 | return initialState;
40 | default:
41 | return state;
42 | }
43 | }
44 |
45 | export function loadSecurity() {
46 | return {
47 | types: [LOADSECURITY_START, LOADSECURITY_COMPLETE, LOADSECURITY_ERROR],
48 | promise: (client) => client.post('/api/manage/security')
49 | };
50 | }
51 |
52 | export function setTwoFactor(enabled) {
53 | return {
54 | types: [SETTWOFACTOR_START, SETTWOFACTOR_COMPLETE, SETTWOFACTOR_ERROR],
55 | promise: (client) => client.post('/api/manage/settwofactor', { data: { enabled } })
56 | };
57 | }
58 |
59 | export function destroySecurity() {
60 | return { type: SECURITY_DESTROY };
61 | }
62 |
--------------------------------------------------------------------------------
/src/ReactBoilerplate/Scripts/redux/modules/viewBag.js:
--------------------------------------------------------------------------------
1 | import { LOCATION_CHANGE } from 'react-router-redux';
2 |
3 | export default function reducer(state = { $internal: false }, action = {}) {
4 | switch (action.type) {
5 | case '_HYDRATE_VIEWBAG':
6 | // This initial data is provided by the server.
7 | // It exists for the initial request only.
8 | return {
9 | ...state,
10 | ...action.viewBag
11 | };
12 | case LOCATION_CHANGE:
13 | // This little snippet is a hack to prevent the viewBag
14 | // from being cleared on initial page load, but destroyed
15 | // when navigating to a different page.
16 | // https://github.com/reactjs/react-router-redux/issues/340
17 | if (state.$internal) return { $internal: true };
18 | return {
19 | ...state,
20 | $internal: true,
21 | ...action.viewBag
22 | };
23 | default:
24 | return state;
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/ReactBoilerplate/Scripts/redux/reducer.js:
--------------------------------------------------------------------------------
1 | import { combineReducers } from 'redux';
2 | import { reducer as formReducer } from 'redux-form';
3 | import auth from './modules/auth';
4 | import account from './modules/account';
5 | import externalLogin from './modules/externalLogin';
6 | import manage from './modules/manage';
7 | import viewBag from './modules/viewBag';
8 | import { routerReducer } from 'react-router-redux';
9 |
10 | export default combineReducers({
11 | form: formReducer,
12 | routing: routerReducer,
13 | auth,
14 | account,
15 | externalLogin,
16 | manage,
17 | viewBag
18 | });
19 |
--------------------------------------------------------------------------------
/src/ReactBoilerplate/Scripts/routes.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { IndexRoute, Route } from 'react-router';
3 | import {
4 | App,
5 | Home,
6 | About,
7 | Contact,
8 | NotFound,
9 | Register,
10 | Login,
11 | ForgotPassword,
12 | ResetPassword,
13 | ConfirmEmail,
14 | Manage,
15 | ManageIndex,
16 | ManageSecurity,
17 | ManageChangePassword,
18 | ManageLogins,
19 | ManageEmail
20 | } from './containers';
21 |
22 | export default (store) => {
23 | const requireLogin = (nextState, replace, cb) => {
24 | const { auth: { user } } = store.getState();
25 | if (!user) {
26 | // oops, not logged in, so can't be here!
27 | replace('/login?returnUrl=' +
28 | encodeURIComponent(nextState.location.pathname + nextState.location.search));
29 | }
30 | cb();
31 | };
32 | return (
33 |
34 | { /* Home (main) route */ }
35 |
36 |
37 | { /* Routes */ }
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 | { /* Catch all route */ }
55 |
56 |
57 | );
58 | };
59 |
--------------------------------------------------------------------------------
/src/ReactBoilerplate/Scripts/server.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom/server';
3 | import Html from './helpers/Html';
4 | import { match } from 'react-router';
5 | import getRoutes from './routes';
6 | import createHistory from 'react-router/lib/createMemoryHistory';
7 | import RouterContext from 'react-router/lib/RouterContext';
8 | import configureStore from './redux/configureStore';
9 | import { Provider } from 'react-redux';
10 | import isEmpty from 'utils/isEmpty';
11 |
12 | export function renderView(callback, path, model, viewBag) {
13 | const history = createHistory(path);
14 | const store = configureStore(model, history);
15 | const result = {
16 | html: null,
17 | status: 404,
18 | redirect: null
19 | };
20 | match(
21 | { history, routes: getRoutes(store), location: path },
22 | (error, redirectLocation, renderProps) => {
23 | if (redirectLocation) {
24 | result.redirect = redirectLocation.pathname + redirectLocation.search;
25 | } else if (error) {
26 | result.status = 500;
27 | } else if (renderProps) {
28 | // if this is the NotFoundRoute, then return a 404
29 | const isNotFound = renderProps.routes.filter((route) => route.status === 404).length > 0;
30 | result.status = isNotFound ? 404 : 200;
31 | const component =
32 | (
33 |
34 |
35 |
36 | );
37 | if (!isEmpty(viewBag)) {
38 | // If the server provided anyhting in ASP.NET's ViewBag, hydrate it to the store/state.
39 | // The contents can be accessed on the client via `state.viewBag`. It exist for the initial
40 | // page load only, and will be cleared when navigating to another page on the client.
41 | store.dispatch({ type: '_HYDRATE_VIEWBAG', viewBag });
42 | }
43 | result.html = ReactDOM.renderToString( );
44 | } else {
45 | result.status = 404;
46 | }
47 | });
48 | callback(null, result);
49 | }
50 |
51 | export function renderPartialView(callback) {
52 | callback('TODO', null);
53 | }
54 |
--------------------------------------------------------------------------------
/src/ReactBoilerplate/Scripts/utils/isEmpty.js:
--------------------------------------------------------------------------------
1 | // Speed up calls to hasOwnProperty
2 | const hasOwnProperty = Object.prototype.hasOwnProperty;
3 |
4 | export default function isEmpty(obj) {
5 | // null and undefined are "empty"
6 | if (obj === null) return true;
7 | // Assume if it has a length property with a non-zero value
8 | // that that property is correct.
9 | if (obj.length > 0) return false;
10 | if (obj.length === 0) return true;
11 | // Otherwise, does it have any properties of its own?
12 | // Note that this doesn't handle
13 | // toString and valueOf enumeration bugs in IE < 9
14 | for (const key in obj) {
15 | if (hasOwnProperty.call(obj, key)) return false;
16 | }
17 | return true;
18 | }
19 |
--------------------------------------------------------------------------------
/src/ReactBoilerplate/Scripts/utils/modelState.js:
--------------------------------------------------------------------------------
1 | import _ from 'lodash';
2 |
3 | /* eslint "import/prefer-default-export": 0 */
4 | /* eslint "no-prototype-builtins": 0 */
5 | /* eslint "no-underscore-dangle": [2, {"allow": ["_global", "_error" ]}] */
6 |
7 | // this method will map model state returned from an API, into an object
8 | // this is valid for passing to redux-forms for validation.
9 | export function modelStateErrorToFormFields(modelState) {
10 | if (!modelState) {
11 | return null;
12 | }
13 | let updatedModelState = _.omit(modelState, '_global');
14 | updatedModelState = _.mapValues(updatedModelState, (value) =>
15 | ({
16 | errors: value
17 | })
18 | );
19 | if (modelState.hasOwnProperty('_global') && modelState._global.length !== 0) {
20 | updatedModelState._error = {
21 | errors: modelState._global
22 | };
23 | } else {
24 | updatedModelState._error = null;
25 | }
26 | return updatedModelState;
27 | }
28 |
--------------------------------------------------------------------------------
/src/ReactBoilerplate/Scripts/utils/promise-window-server.js:
--------------------------------------------------------------------------------
1 | export default class PromiseWindow {
2 | open() {
3 | throw new Error('you can not run this on the server, ding-dong.');
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/src/ReactBoilerplate/Scripts/utils/superagent-server.js:
--------------------------------------------------------------------------------
1 | // this replaces superagent on the server.
2 | // TODO: If the server attempts to use this, throw an exception.
3 | // This will ensure that not server-side-rendering depends on network requests.
4 | // The entire state should have been provided for the page already.
5 |
--------------------------------------------------------------------------------
/src/ReactBoilerplate/Scripts/webpack/dev.config.js:
--------------------------------------------------------------------------------
1 | var fs = require('fs');
2 | var babelrc = JSON.parse(fs.readFileSync('./.babelrc'));
3 | var ExtractTextPlugin = require('extract-text-webpack-plugin');
4 | var extractCSS = new ExtractTextPlugin('styles.css');
5 | var webpack = require('webpack');
6 | var path = require('path');
7 |
8 | module.exports = {
9 | server: {
10 | entry: {
11 | server: [
12 | path.resolve(__dirname, '..', '..', 'Scripts', 'server.js')
13 | ]
14 | },
15 | resolve: {
16 | modulesDirectories: [
17 | 'Scripts',
18 | 'node_modules'
19 | ],
20 | alias: {
21 | 'superagent': path.resolve(__dirname, '..', 'utils', 'superagent-server.js'),
22 | 'promise-window': path.resolve(__dirname, '..', 'utils', 'promise-window-server.js')
23 | }
24 | },
25 | module: {
26 | loaders: [
27 | { test: /\.jsx?$/, exclude: /node_modules/, loaders: ['babel?' + JSON.stringify(babelrc), 'eslint'] },
28 | { test: /\.css$/, loader: 'css/locals?module' },
29 | { test: /\.scss$/, loader: 'css/locals?module!sass' },
30 | { test: /\.(woff2?|ttf|eot|svg)(\?v=[0-9]\.[0-9]\.[0-9])?$/, loader: 'file' },
31 | { test: /\.(jpeg|jpeg|gif|png|tiff)(\?v=[0-9]\.[0-9]\.[0-9])?$/, loader: 'file' }
32 | ]
33 | },
34 | output: {
35 | filename: '[name].generated.js',
36 | libraryTarget: 'this',
37 | path: path.resolve(__dirname, '..', '..', 'wwwroot', 'pack'),
38 | publicPath: '/pack/'
39 | },
40 | plugins: [
41 | new webpack.DefinePlugin({
42 | __CLIENT__: false,
43 | __SERVER__: true
44 | })
45 | ],
46 | },
47 | client: {
48 | entry: {
49 | client: [
50 | 'bootstrap-loader',
51 | path.resolve(__dirname, '..', '..', 'Scripts', 'client.js')
52 | ]
53 | },
54 | resolve: {
55 | modulesDirectories: [
56 | 'Scripts',
57 | 'node_modules'
58 | ]
59 | },
60 | module: {
61 | loaders: [
62 | { test: /\.jsx?$/, exclude: /node_modules/, loaders: ['babel?' + JSON.stringify(babelrc), 'eslint'] },
63 | { test: /\.css$/, loader: extractCSS.extract('style', 'css?modules') },
64 | { test: /\.scss$/, loader: extractCSS.extract('style', 'css?modules!sass') },
65 | { test: /\.(woff2?|ttf|eot|svg)(\?v=[0-9]\.[0-9]\.[0-9])?$/, loader: 'file' },
66 | { test: /\.(jpeg|jpeg|gif|png|tiff)(\?v=[0-9]\.[0-9]\.[0-9])?$/, loader: 'file' }
67 | ]
68 | },
69 | output: {
70 | filename: '[name].generated.js',
71 | libraryTarget: 'this',
72 | path: path.resolve(__dirname, '..', '..', 'wwwroot', 'pack'),
73 | publicPath: '/pack/'
74 | },
75 | plugins: [
76 | extractCSS,
77 | new webpack.DefinePlugin({
78 | __CLIENT__: true,
79 | __SERVER__: false
80 | })
81 | ],
82 | devtool: 'source-map'
83 | }
84 | };
85 |
--------------------------------------------------------------------------------
/src/ReactBoilerplate/Scripts/webpack/prod.config.js:
--------------------------------------------------------------------------------
1 | var fs = require('fs');
2 | var babelrc = JSON.parse(fs.readFileSync('./.babelrc'));
3 | var ExtractTextPlugin = require('extract-text-webpack-plugin');
4 | var extractCSS = new ExtractTextPlugin('styles.css');
5 | var webpack = require('webpack');
6 | var path = require('path');
7 |
8 | module.exports = {
9 | server : {
10 | entry: {
11 | server: [
12 | path.resolve(__dirname, '..', '..', 'Scripts', 'server.js')
13 | ]
14 | },
15 | resolve: {
16 | modulesDirectories: [
17 | 'Scripts',
18 | 'node_modules'
19 | ],
20 | alias: {
21 | 'superagent': path.resolve(__dirname, '..', 'utils', 'superagent-server.js'),
22 | 'promise-window': path.resolve(__dirname, '..', 'utils', 'promise-window-server.js')
23 | },
24 | },
25 | module: {
26 | loaders: [
27 | { test: /\.jsx?$/, exclude: /node_modules/, loaders: ['babel?' + JSON.stringify(babelrc), 'eslint'] },
28 | { test: /\.css$/, loader: 'css/locals?module' },
29 | { test: /\.scss$/, loader: 'css/locals?module!sass' },
30 | { test: /\.(woff2?|ttf|eot|svg)(\?v=[0-9]\.[0-9]\.[0-9])?$/, loader: 'file' },
31 | { test: /\.(jpeg|jpeg|gif|png|tiff)(\?v=[0-9]\.[0-9]\.[0-9])?$/, loader: 'file' }
32 | ]
33 | },
34 | output: {
35 | filename: '[name].generated.js',
36 | libraryTarget: 'this',
37 | path: path.resolve(__dirname, '..', '..', 'wwwroot', 'pack'),
38 | publicPath: '/pack/'
39 | },
40 | plugins: [
41 | new webpack.optimize.DedupePlugin(),
42 | new webpack.optimize.OccurenceOrderPlugin(),
43 | new webpack.optimize.UglifyJsPlugin({
44 | compress: {
45 | warnings: false
46 | }
47 | }),
48 | new webpack.DefinePlugin({
49 | 'process.env': {
50 | NODE_ENV: '"production"'
51 | },
52 | __CLIENT__: false,
53 | __SERVER__: true
54 | })
55 | ]
56 | },
57 | client: {
58 | entry: {
59 | 'client': [
60 | 'bootstrap-loader',
61 | path.resolve(__dirname, '..', '..', 'Scripts', 'client.js')
62 | ]
63 | },
64 | resolve: {
65 | modulesDirectories: [
66 | 'Scripts',
67 | 'node_modules'
68 | ]
69 | },
70 | module: {
71 | loaders: [
72 | { test: /\.jsx?$/, exclude: /node_modules/, loaders: ['babel?' + JSON.stringify(babelrc), 'eslint'] },
73 | { test: /\.css$/, loader: extractCSS.extract('style', 'css?modules') },
74 | { test: /\.scss$/, loader: extractCSS.extract('style', 'css?modules!sass') },
75 | { test: /\.(woff2?|ttf|eot|svg)(\?v=[0-9]\.[0-9]\.[0-9])?$/, loader: 'file' },
76 | { test: /\.(jpeg|jpeg|gif|png|tiff)(\?v=[0-9]\.[0-9]\.[0-9])?$/, loader: 'file' }
77 | ]
78 | },
79 | output: {
80 | filename: '[name].generated.js',
81 | libraryTarget: 'this',
82 | path: path.resolve(__dirname, '..', '..', 'wwwroot', 'pack'),
83 | publicPath: '/pack/'
84 | },
85 | plugins: [
86 | extractCSS,
87 | new webpack.optimize.DedupePlugin(),
88 | new webpack.optimize.OccurenceOrderPlugin(),
89 | new webpack.optimize.UglifyJsPlugin({
90 | compress: {
91 | warnings: false
92 | }
93 | }),
94 | new webpack.DefinePlugin({
95 | 'process.env': {
96 | NODE_ENV: '"production"'
97 | },
98 | __CLIENT__: true,
99 | __SERVER__: false
100 | })
101 | ]
102 | }
103 | };
104 |
--------------------------------------------------------------------------------
/src/ReactBoilerplate/Services/EmailSender.cs:
--------------------------------------------------------------------------------
1 | using System.Threading.Tasks;
2 | using Microsoft.Extensions.Logging;
3 |
4 | namespace ReactBoilerplate.Services
5 | {
6 | public class EmailSender : IEmailSender
7 | {
8 | ILogger _logger;
9 |
10 | public EmailSender(ILoggerFactory loggerFactory)
11 | {
12 | _logger = loggerFactory.CreateLogger