Skip to content
Open

Ht2 #14

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion admin/src/components/auth/sign-in-form.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,11 @@ class SignInForm extends Component {
<Field component="input" name="password" type="password" />
</div>
</div>
<button type="submit">Sign In</button>
{this.props.tooManySignInAttempts ? (
<div>BAN!!</div>
) : (
<button type="submit">Sign In</button>
)}
</form>
</div>
)
Expand Down
15 changes: 11 additions & 4 deletions admin/src/components/routes/auth/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { Route, NavLink } from 'react-router-dom'
import { connect } from 'react-redux'
import SignInForm from '../../auth/sign-in-form'
import SignUpForm from '../../auth/sign-up-form'
import { signIn, signUp } from '../../../ducks/auth'
import { signIn, signUp, tooManySignInAttempts } from '../../../ducks/auth'

class AuthPage extends Component {
static propTypes = {}
Expand All @@ -18,21 +18,28 @@ class AuthPage extends Component {
<div>
<NavLink to="/auth/sign-up">Sign Up</NavLink>
</div>

<Route path="/auth/sign-in" render={this.getSignInForm} />
<Route path="/auth/sign-up" render={this.getSignUpForm} />
</div>
)
}

getSignInForm = () => <SignInForm onSubmit={this.handleSignIn} />
getSignInForm = () => (
<SignInForm
tooManySignInAttempts={this.props.tooManySignInAttempts}
onSubmit={this.handleSignIn}
/>
)
getSignUpForm = () => <SignUpForm onSubmit={this.handleSignUp} />

handleSignIn = ({ email, password }) => this.props.signIn(email, password)
handleSignUp = ({ email, password }) => this.props.signUp(email, password)
}
const mapStateToProps = (state) => ({
tooManySignInAttempts: tooManySignInAttempts(state)
})

export default connect(
null,
mapStateToProps,
{ signIn, signUp }
)(AuthPage)
65 changes: 53 additions & 12 deletions admin/src/ducks/auth.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { appName } from '../config'
import { Record } from 'immutable'
import { createSelector } from 'reselect'
import { takeEvery, call, put, all } from 'redux-saga/effects'
import { takeEvery, call, put, all, select } from 'redux-saga/effects'
import api from '../services/api'

/**
Expand All @@ -11,16 +11,22 @@ export const moduleName = 'auth'
const prefix = `${appName}/${moduleName}`

export const SIGN_IN_REQUEST = `${prefix}/SIGN_IN_REQUEST`
export const SIGN_IN_ATTEMPT = `${prefix}/SIGN_IN_ATTEMPT`
export const SIGN_IN_ATTEMPT_RESET = `${prefix}/SIGN_IN_ATTEMPT_RESET`
export const SIGN_UP_REQUEST = `${prefix}/SIGN_UP_REQUEST`
export const SIGN_IN_SUCCESS = `${prefix}/SIGN_IN_SUCCESS`
export const SIGN_IN_ERROR = `${prefix}/SIGN_IN_ERROR`
export const SIGN_UP_SUCCESS = `${prefix}/SIGN_UP_SUCCESS`
export const SIGN_UP_ERROR = `${prefix}/SIGN_UP_ERROR`
export const AUTH_STATE_CHANGE = `${prefix}/AUTH_STATE_CHANGE`

export const MAX_SIGN_IN_ATTEMPT_COUNT = 2
/**
* Reducer
* */
export const ReducerRecord = Record({
user: null
user: null,
signInAttempt: 0
})

export default function reducer(state = new ReducerRecord(), action) {
Expand All @@ -31,12 +37,21 @@ export default function reducer(state = new ReducerRecord(), action) {
case SIGN_UP_SUCCESS:
case AUTH_STATE_CHANGE:
return state.set('user', payload.user)

case SIGN_IN_ATTEMPT:
return state.set('signInAttempt', state.get('signInAttempt') + 1)
case SIGN_IN_ATTEMPT_RESET:
return state.set('signInAttempt', 0)
default:
return state
}
}

export const signInAttempt = (state) => state[moduleName].signInAttempt
export const tooManySignInAttempts = createSelector(
signInAttempt,
(count) => count > MAX_SIGN_IN_ATTEMPT_COUNT
)

/**
* Selectors
* */
Expand Down Expand Up @@ -70,14 +85,18 @@ export function signIn(email, password) {
}
}

export function signInError(error) {
return {
type: SIGN_IN_ERROR,
error
}
}

export function signUp(email, password) {
return (dispatch) =>
api.signUp(email, password).then((user) =>
dispatch({
type: SIGN_UP_SUCCESS,
payload: { user }
})
)
return {
type: SIGN_UP_REQUEST,
payload: { email, password }
}
}

/**
Expand All @@ -92,14 +111,36 @@ export function* signInSaga({ payload: { email, password } }) {
type: SIGN_IN_SUCCESS,
payload: { user }
})
} catch (error) {
yield put(signInError(error))
}
}

export function* signInErrorSaga(action) {
yield put({ type: SIGN_IN_ATTEMPT })
const tooManyAttempts = yield select(tooManySignInAttempts)
}

export function* signUpSaga({ payload: { email, password } }) {
try {
const user = yield call(api.signUp, email, password)

yield put({
type: SIGN_UP_SUCCESS,
payload: { user }
})
} catch (error) {
yield put({
type: SIGN_IN_ERROR,
type: SIGN_UP_ERROR,
error
})
}
}

export function* saga() {
yield all([takeEvery(SIGN_IN_REQUEST, signInSaga)])
yield all([
takeEvery(SIGN_IN_REQUEST, signInSaga),
takeEvery(SIGN_UP_REQUEST, signUpSaga),
takeEvery(SIGN_IN_ERROR, signInErrorSaga)
])
}
Loading