Client: login/register
Client: login/register Client: login/register Client: login/register Client: login/register Client: login/register Client: login/register Client: login/register Client: login/register
This commit is contained in:
parent
166cbd201a
commit
3b13ee67c0
@ -32,3 +32,16 @@ if app.debug:
|
|||||||
).handlers = logging.getLogger('werkzeug').handlers
|
).handlers = logging.getLogger('werkzeug').handlers
|
||||||
logging.getLogger('sqlalchemy.orm').setLevel(logging.WARNING)
|
logging.getLogger('sqlalchemy.orm').setLevel(logging.WARNING)
|
||||||
appLog.setLevel(logging.DEBUG)
|
appLog.setLevel(logging.DEBUG)
|
||||||
|
|
||||||
|
if app.debug :
|
||||||
|
# Enable CORS
|
||||||
|
@app.after_request
|
||||||
|
def after_request(response):
|
||||||
|
response.headers.add('Access-Control-Allow-Origin', '*')
|
||||||
|
response.headers.add(
|
||||||
|
'Access-Control-Allow-Headers', 'Content-Type,Authorization'
|
||||||
|
)
|
||||||
|
response.headers.add(
|
||||||
|
'Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE,PATCH,OPTIONS'
|
||||||
|
)
|
||||||
|
return response
|
||||||
|
@ -191,3 +191,42 @@ class TestAuthBlueprint(BaseTestCase):
|
|||||||
self.assertTrue(
|
self.assertTrue(
|
||||||
data['message'] == 'Invalid token. Please log in again.')
|
data['message'] == 'Invalid token. Please log in again.')
|
||||||
self.assertEqual(response.status_code, 401)
|
self.assertEqual(response.status_code, 401)
|
||||||
|
|
||||||
|
def test_user_profile(self):
|
||||||
|
add_user('test', 'test@test.com', 'test')
|
||||||
|
with self.client:
|
||||||
|
resp_login = self.client.post(
|
||||||
|
'/api/auth/login',
|
||||||
|
data=json.dumps(dict(
|
||||||
|
email='test@test.com',
|
||||||
|
password='test'
|
||||||
|
)),
|
||||||
|
content_type='application/json'
|
||||||
|
)
|
||||||
|
response = self.client.get(
|
||||||
|
'/api/auth/profile',
|
||||||
|
headers=dict(
|
||||||
|
Authorization='Bearer ' + json.loads(
|
||||||
|
resp_login.data.decode()
|
||||||
|
)['auth_token']
|
||||||
|
)
|
||||||
|
)
|
||||||
|
data = json.loads(response.data.decode())
|
||||||
|
self.assertTrue(data['status'] == 'success')
|
||||||
|
self.assertTrue(data['data'] is not None)
|
||||||
|
self.assertTrue(data['data']['username'] == 'test')
|
||||||
|
self.assertTrue(data['data']['email'] == 'test@test.com')
|
||||||
|
self.assertTrue(data['data']['created_at'])
|
||||||
|
self.assertFalse(data['data']['admin'])
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
|
def test_invalid_profile(self):
|
||||||
|
with self.client:
|
||||||
|
response = self.client.get(
|
||||||
|
'/api/auth/profile',
|
||||||
|
headers=dict(Authorization='Bearer invalid'))
|
||||||
|
data = json.loads(response.data.decode())
|
||||||
|
self.assertTrue(data['status'] == 'error')
|
||||||
|
self.assertTrue(
|
||||||
|
data['message'] == 'Invalid token. Please log in again.')
|
||||||
|
self.assertEqual(response.status_code, 401)
|
||||||
|
@ -4,6 +4,7 @@ from sqlalchemy import exc, or_
|
|||||||
from mpwo_api import appLog, bcrypt, db
|
from mpwo_api import appLog, bcrypt, db
|
||||||
|
|
||||||
from .models import User
|
from .models import User
|
||||||
|
from .utils import authenticate
|
||||||
|
|
||||||
auth_blueprint = Blueprint('auth', __name__)
|
auth_blueprint = Blueprint('auth', __name__)
|
||||||
|
|
||||||
@ -12,7 +13,9 @@ auth_blueprint = Blueprint('auth', __name__)
|
|||||||
def register_user():
|
def register_user():
|
||||||
# get post data
|
# get post data
|
||||||
post_data = request.get_json()
|
post_data = request.get_json()
|
||||||
if not post_data:
|
if not post_data or post_data.get('username') is None \
|
||||||
|
or post_data.get('email') is None \
|
||||||
|
or post_data.get('password') is None:
|
||||||
response_object = {
|
response_object = {
|
||||||
'status': 'error',
|
'status': 'error',
|
||||||
'message': 'Invalid payload.'
|
'message': 'Invalid payload.'
|
||||||
@ -52,9 +55,10 @@ def register_user():
|
|||||||
except (exc.IntegrityError, exc.OperationalError, ValueError) as e:
|
except (exc.IntegrityError, exc.OperationalError, ValueError) as e:
|
||||||
db.session.rollback()
|
db.session.rollback()
|
||||||
appLog.error(e)
|
appLog.error(e)
|
||||||
|
|
||||||
response_object = {
|
response_object = {
|
||||||
'status': 'error',
|
'status': 'error',
|
||||||
'message': 'Invalid payload.'
|
'message': 'Error. Please try again or contact the administrator.'
|
||||||
}
|
}
|
||||||
return jsonify(response_object), 400
|
return jsonify(response_object), 400
|
||||||
|
|
||||||
@ -95,19 +99,20 @@ def login_user():
|
|||||||
appLog.error(e)
|
appLog.error(e)
|
||||||
response_object = {
|
response_object = {
|
||||||
'status': 'error',
|
'status': 'error',
|
||||||
'message': 'Try again'
|
'message': 'Error. Please try again or contact the administrator.'
|
||||||
}
|
}
|
||||||
return jsonify(response_object), 500
|
return jsonify(response_object), 500
|
||||||
|
|
||||||
|
|
||||||
@auth_blueprint.route('/auth/logout', methods=['GET'])
|
@auth_blueprint.route('/auth/logout', methods=['GET'])
|
||||||
def logout_user():
|
@authenticate
|
||||||
|
def logout_user(user_id):
|
||||||
# get auth token
|
# get auth token
|
||||||
auth_header = request.headers.get('Authorization')
|
auth_header = request.headers.get('Authorization')
|
||||||
if auth_header:
|
if auth_header:
|
||||||
auth_token = auth_header.split(" ")[1]
|
auth_token = auth_header.split(" ")[1]
|
||||||
resp = User.decode_auth_token(auth_token)
|
resp = User.decode_auth_token(auth_token)
|
||||||
if not isinstance(resp, str):
|
if not isinstance(user_id, str):
|
||||||
response_object = {
|
response_object = {
|
||||||
'status': 'success',
|
'status': 'success',
|
||||||
'message': 'Successfully logged out.'
|
'message': 'Successfully logged out.'
|
||||||
@ -125,3 +130,20 @@ def logout_user():
|
|||||||
'message': 'Provide a valid auth token.'
|
'message': 'Provide a valid auth token.'
|
||||||
}
|
}
|
||||||
return jsonify(response_object), 403
|
return jsonify(response_object), 403
|
||||||
|
|
||||||
|
|
||||||
|
@auth_blueprint.route('/auth/profile', methods=['GET'])
|
||||||
|
@authenticate
|
||||||
|
def get_user_status(user_id):
|
||||||
|
user = User.query.filter_by(id=user_id).first()
|
||||||
|
response_object = {
|
||||||
|
'status': 'success',
|
||||||
|
'data': {
|
||||||
|
'id': user.id,
|
||||||
|
'username': user.username,
|
||||||
|
'email': user.email,
|
||||||
|
'created_at': user.created_at,
|
||||||
|
'admin': user.admin,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return jsonify(response_object), 200
|
||||||
|
35
mpwo_api/mpwo_api/users/utils.py
Normal file
35
mpwo_api/mpwo_api/users/utils.py
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
from functools import wraps
|
||||||
|
|
||||||
|
from flask import request, jsonify
|
||||||
|
|
||||||
|
from .models import User
|
||||||
|
|
||||||
|
|
||||||
|
def authenticate(f):
|
||||||
|
@wraps(f)
|
||||||
|
def decorated_function(*args, **kwargs):
|
||||||
|
response_object = {
|
||||||
|
'status': 'error',
|
||||||
|
'message': 'Something went wrong. Please contact us.'
|
||||||
|
}
|
||||||
|
code = 401
|
||||||
|
auth_header = request.headers.get('Authorization')
|
||||||
|
if not auth_header:
|
||||||
|
response_object['message'] = 'Provide a valid auth token.'
|
||||||
|
code = 403
|
||||||
|
return jsonify(response_object), code
|
||||||
|
auth_token = auth_header.split(" ")[1]
|
||||||
|
resp = User.decode_auth_token(auth_token)
|
||||||
|
if isinstance(resp, str):
|
||||||
|
response_object['message'] = resp
|
||||||
|
return jsonify(response_object), code
|
||||||
|
user = User.query.filter_by(id=resp).first()
|
||||||
|
if not user:
|
||||||
|
return jsonify(response_object), code
|
||||||
|
return f(resp, *args, **kwargs)
|
||||||
|
return decorated_function
|
||||||
|
|
||||||
|
|
||||||
|
def is_admin(user_id):
|
||||||
|
user = User.query.filter_by(id=user_id).first()
|
||||||
|
return user.admin
|
@ -0,0 +1,117 @@
|
|||||||
|
import mpwoApi from '../mpwoApi'
|
||||||
|
|
||||||
|
function AuthError(message) {
|
||||||
|
return { type: 'AUTH_ERROR', message }
|
||||||
|
}
|
||||||
|
|
||||||
|
function ProfileSuccess(message) {
|
||||||
|
return { type: 'PROFILE_SUCCESS', message }
|
||||||
|
}
|
||||||
|
|
||||||
|
function ProfileError(message) {
|
||||||
|
return { type: 'PROFILE_ERROR', message }
|
||||||
|
}
|
||||||
|
|
||||||
|
const updateFormDataEmail = value => ({
|
||||||
|
type: 'UPDATE_FORMDATA_EMAIL',
|
||||||
|
email: value,
|
||||||
|
})
|
||||||
|
|
||||||
|
const updateFormDataUsername = value => ({
|
||||||
|
type: 'UPDATE_FORMDATA_USERNAME',
|
||||||
|
username: value,
|
||||||
|
})
|
||||||
|
|
||||||
|
const updateFormDataPassword = value => ({
|
||||||
|
type: 'UPDATE_FORMDATA_PASSWORD',
|
||||||
|
password: value,
|
||||||
|
})
|
||||||
|
|
||||||
|
export function getProfile(dispatch) {
|
||||||
|
return mpwoApi
|
||||||
|
.getProfile()
|
||||||
|
.then(ret => {
|
||||||
|
if (ret.status === 'success') {
|
||||||
|
dispatch(ProfileSuccess(ret))
|
||||||
|
} else {
|
||||||
|
dispatch(ProfileError(ret.message))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
throw error
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function register(formData) {
|
||||||
|
return function(dispatch) {
|
||||||
|
return mpwoApi
|
||||||
|
.register(formData.username, formData.email, formData.password)
|
||||||
|
.then(ret => {
|
||||||
|
if (ret.status === 'success') {
|
||||||
|
window.localStorage.setItem('authToken', ret.auth_token)
|
||||||
|
getProfile(dispatch)
|
||||||
|
} else {
|
||||||
|
dispatch(AuthError(ret.message))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
throw error
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function login(formData) {
|
||||||
|
return function(dispatch) {
|
||||||
|
return mpwoApi
|
||||||
|
.login(formData.email, formData.password)
|
||||||
|
.then(ret => {
|
||||||
|
if (ret.status === 'success') {
|
||||||
|
window.localStorage.setItem('authToken', ret.auth_token)
|
||||||
|
getProfile(dispatch)
|
||||||
|
} else {
|
||||||
|
dispatch(AuthError(ret.message))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
throw error
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function loadProfile() {
|
||||||
|
if (window.localStorage.getItem('authToken')) {
|
||||||
|
return function(dispatch) {
|
||||||
|
getProfile(dispatch)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return { type: 'LOGOUT' }
|
||||||
|
}
|
||||||
|
|
||||||
|
export function logout() {
|
||||||
|
return { type: 'LOGOUT' }
|
||||||
|
}
|
||||||
|
|
||||||
|
export function handleUserFormSubmit(event, formType) {
|
||||||
|
event.preventDefault()
|
||||||
|
return (dispatch, getState) => {
|
||||||
|
const state = getState()
|
||||||
|
let { formData } = state.formData
|
||||||
|
formData.formData = state.formData.formData
|
||||||
|
if (formType === 'Login') {
|
||||||
|
dispatch(login(formData.formData))
|
||||||
|
} else { // formType === 'Register'
|
||||||
|
dispatch(register(formData.formData))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const handleFormChange = event => dispatch => {
|
||||||
|
switch (event.target.name) {
|
||||||
|
case 'email':
|
||||||
|
return dispatch(updateFormDataEmail(event.target.value))
|
||||||
|
case 'username':
|
||||||
|
return dispatch(updateFormDataUsername(event.target.value))
|
||||||
|
default: // case 'password':
|
||||||
|
return dispatch(updateFormDataPassword(event.target.value))
|
||||||
|
}
|
||||||
|
}
|
@ -1,7 +1,10 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
import { Route, Switch } from 'react-router-dom'
|
||||||
|
|
||||||
import './App.css'
|
import './App.css'
|
||||||
|
import Logout from './Logout'
|
||||||
import NavBar from './NavBar'
|
import NavBar from './NavBar'
|
||||||
|
import UserForm from './User/UserForm'
|
||||||
|
|
||||||
export default class App extends React.Component {
|
export default class App extends React.Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
@ -13,9 +16,37 @@ export default class App extends React.Component {
|
|||||||
return (
|
return (
|
||||||
<div className="App">
|
<div className="App">
|
||||||
<NavBar />
|
<NavBar />
|
||||||
<p className="App-body">
|
<div className="container">
|
||||||
App in progress
|
<div className="row">
|
||||||
</p>
|
<div className="col-md-6">
|
||||||
|
<br />
|
||||||
|
<Switch>
|
||||||
|
<Route
|
||||||
|
exact path="/register"
|
||||||
|
render={() => (
|
||||||
|
<UserForm
|
||||||
|
formType={'Register'}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<Route
|
||||||
|
exact path="/login"
|
||||||
|
render={() => (
|
||||||
|
<UserForm
|
||||||
|
formType={'Login'}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<Route
|
||||||
|
exact path="/logout"
|
||||||
|
render={() => (
|
||||||
|
<Logout />
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</Switch>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
31
mpwo_client/src/components/Logout.jsx
Normal file
31
mpwo_client/src/components/Logout.jsx
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import { connect } from 'react-redux'
|
||||||
|
import { Link } from 'react-router-dom'
|
||||||
|
|
||||||
|
import { logout } from '../actions'
|
||||||
|
|
||||||
|
class Logout extends React.Component {
|
||||||
|
componentDidMount() {
|
||||||
|
this.props.UserLogout()
|
||||||
|
}
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<p>
|
||||||
|
You are now logged out.
|
||||||
|
Click <Link to="/login">here</Link> to log back in.</p>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default connect(
|
||||||
|
state => ({
|
||||||
|
user: state.user,
|
||||||
|
}),
|
||||||
|
dispatch => ({
|
||||||
|
UserLogout: () => {
|
||||||
|
dispatch(logout())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
)(Logout)
|
@ -1,13 +1,8 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
import { connect } from 'react-redux'
|
||||||
import { Link } from 'react-router-dom'
|
import { Link } from 'react-router-dom'
|
||||||
|
|
||||||
export default class NavBar extends React.Component {
|
function NavBar (props) {
|
||||||
constructor(props) {
|
|
||||||
super(props)
|
|
||||||
this.props = props
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
return (
|
return (
|
||||||
<header>
|
<header>
|
||||||
<nav className="navbar navbar-expand-lg navbar-light bg-light">
|
<nav className="navbar navbar-expand-lg navbar-light bg-light">
|
||||||
@ -36,10 +31,50 @@ export default class NavBar extends React.Component {
|
|||||||
Home
|
Home
|
||||||
</Link>
|
</Link>
|
||||||
</li>
|
</li>
|
||||||
|
{!props.user.isAuthenticated && (
|
||||||
|
<li className="nav-item">
|
||||||
|
<Link
|
||||||
|
className="nav-link"
|
||||||
|
to={{
|
||||||
|
pathname: '/login',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Login
|
||||||
|
</Link>
|
||||||
|
</li>
|
||||||
|
)}
|
||||||
|
{!props.user.isAuthenticated && (
|
||||||
|
<li className="nav-item">
|
||||||
|
<Link
|
||||||
|
className="nav-link"
|
||||||
|
to={{
|
||||||
|
pathname: '/register',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Register
|
||||||
|
</Link>
|
||||||
|
</li>
|
||||||
|
)}
|
||||||
|
{props.user.isAuthenticated && (
|
||||||
|
<li className="nav-item">
|
||||||
|
<Link
|
||||||
|
className="nav-link"
|
||||||
|
to={{
|
||||||
|
pathname: '/logout',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Logout
|
||||||
|
</Link>
|
||||||
|
</li>
|
||||||
|
)}
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
</header>
|
</header>
|
||||||
)
|
)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
export default connect(
|
||||||
|
state => ({
|
||||||
|
user: state.user,
|
||||||
|
})
|
||||||
|
)(NavBar)
|
||||||
|
52
mpwo_client/src/components/User/Form.jsx
Normal file
52
mpwo_client/src/components/User/Form.jsx
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
|
||||||
|
export default function Form (props) {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<h1>{props.formType}</h1>
|
||||||
|
<hr /><br />
|
||||||
|
<form onSubmit={event =>
|
||||||
|
props.handleUserFormSubmit(event, props.formType)}
|
||||||
|
>
|
||||||
|
{props.formType === 'Register' &&
|
||||||
|
<div className="form-group">
|
||||||
|
<input
|
||||||
|
name="username"
|
||||||
|
className="form-control input-lg"
|
||||||
|
type="text"
|
||||||
|
placeholder="Enter a username"
|
||||||
|
required
|
||||||
|
onChange={props.onHandleFormChange}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
<div className="form-group">
|
||||||
|
<input
|
||||||
|
name="email"
|
||||||
|
className="form-control input-lg"
|
||||||
|
type="email"
|
||||||
|
placeholder="Enter an email address"
|
||||||
|
required
|
||||||
|
onChange={props.onHandleFormChange}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="form-group">
|
||||||
|
<input
|
||||||
|
name="password"
|
||||||
|
className="form-control input-lg"
|
||||||
|
type="password"
|
||||||
|
placeholder="Enter a password"
|
||||||
|
required
|
||||||
|
onChange={props.onHandleFormChange}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<input
|
||||||
|
type="submit"
|
||||||
|
className="btn btn-primary btn-lg btn-block"
|
||||||
|
value="Submit"
|
||||||
|
/>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
36
mpwo_client/src/components/User/UserForm.jsx
Normal file
36
mpwo_client/src/components/User/UserForm.jsx
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import { connect } from 'react-redux'
|
||||||
|
|
||||||
|
import Form from './Form'
|
||||||
|
import { handleFormChange, handleUserFormSubmit } from '../../actions'
|
||||||
|
|
||||||
|
function UserForm(props) {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{props.message !== '' && (
|
||||||
|
<code>{props.message}</code>
|
||||||
|
)}
|
||||||
|
<Form
|
||||||
|
formType={props.formType}
|
||||||
|
userForm={props.formData}
|
||||||
|
onHandleFormChange={event => props.onHandleFormChange(event)}
|
||||||
|
handleUserFormSubmit={(event, formType) =>
|
||||||
|
props.onHandleUserFormSubmit(event, formType)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
export default connect(
|
||||||
|
state => ({
|
||||||
|
formData: state.formData,
|
||||||
|
message: state.message,
|
||||||
|
}),
|
||||||
|
dispatch => ({
|
||||||
|
onHandleFormChange: event => {
|
||||||
|
dispatch(handleFormChange(event))
|
||||||
|
},
|
||||||
|
onHandleUserFormSubmit: (event, formType) => {
|
||||||
|
dispatch(handleUserFormSubmit(event, formType))
|
||||||
|
},
|
||||||
|
})
|
||||||
|
)(UserForm)
|
@ -10,7 +10,7 @@ import App from './components/App'
|
|||||||
import Root from './components/Root'
|
import Root from './components/Root'
|
||||||
import registerServiceWorker from './registerServiceWorker'
|
import registerServiceWorker from './registerServiceWorker'
|
||||||
import reducers from './reducers'
|
import reducers from './reducers'
|
||||||
|
import { loadProfile } from './actions'
|
||||||
|
|
||||||
export const history = createBrowserHistory()
|
export const history = createBrowserHistory()
|
||||||
|
|
||||||
@ -24,6 +24,10 @@ export const store = createStore(
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if (window.localStorage.getItem('authToken') !== null) {
|
||||||
|
store.dispatch(loadProfile())
|
||||||
|
}
|
||||||
|
|
||||||
ReactDOM.render(
|
ReactDOM.render(
|
||||||
<Root store={store} history={history}>
|
<Root store={store} history={history}>
|
||||||
<App />
|
<App />
|
||||||
|
48
mpwo_client/src/mpwoApi.js
Normal file
48
mpwo_client/src/mpwoApi.js
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
const apiUrl = `${process.env.REACT_APP_API_URL}`
|
||||||
|
|
||||||
|
|
||||||
|
export default class MpwoApi {
|
||||||
|
static login(email, password) {
|
||||||
|
const request = new Request(`${apiUrl}auth/login`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: new Headers({
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
}),
|
||||||
|
body: JSON.stringify({
|
||||||
|
email: email,
|
||||||
|
password: password,
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
return fetch(request)
|
||||||
|
.then(response => response.json())
|
||||||
|
.catch(error => error)
|
||||||
|
}
|
||||||
|
static register(username, email, password) {
|
||||||
|
const request = new Request(`${apiUrl}auth/register`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: new Headers({
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
}),
|
||||||
|
body: JSON.stringify({
|
||||||
|
username: username,
|
||||||
|
email: email,
|
||||||
|
password: password,
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
return fetch(request)
|
||||||
|
.then(response => response.json())
|
||||||
|
.catch(error => error)
|
||||||
|
}
|
||||||
|
static getProfile() {
|
||||||
|
const request = new Request(`${apiUrl}auth/profile`, {
|
||||||
|
method: 'GET',
|
||||||
|
headers: new Headers({
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
Authorization: `Bearer ${window.localStorage.getItem('authToken')}`,
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
return fetch(request)
|
||||||
|
.then(response => response.json())
|
||||||
|
.catch(error => error)
|
||||||
|
}
|
||||||
|
}
|
@ -1,9 +1,76 @@
|
|||||||
import { combineReducers } from 'redux'
|
import { combineReducers } from 'redux'
|
||||||
|
|
||||||
import user from './user'
|
import initial from './initial'
|
||||||
|
|
||||||
|
const message = (state = initial.message, action) => {
|
||||||
|
switch (action.type) {
|
||||||
|
case 'AUTH_ERROR':
|
||||||
|
case 'PROFILE_ERROR':
|
||||||
|
return action.message
|
||||||
|
case 'LOGOUT':
|
||||||
|
return ''
|
||||||
|
case 'PROFILE_SUCCESS':
|
||||||
|
return ''
|
||||||
|
default:
|
||||||
|
return state
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const user = (state = initial.user, action) => {
|
||||||
|
switch (action.type) {
|
||||||
|
case 'AUTH_ERROR':
|
||||||
|
case 'PROFILE_ERROR':
|
||||||
|
case 'LOGOUT':
|
||||||
|
window.localStorage.removeItem('authToken')
|
||||||
|
return initial.user
|
||||||
|
case 'PROFILE_SUCCESS':
|
||||||
|
return {
|
||||||
|
id: action.message.data.id,
|
||||||
|
username: action.message.data.username,
|
||||||
|
email: action.message.data.email,
|
||||||
|
isAdmin: action.message.data.admin,
|
||||||
|
createdAt: action.message.data.created_at,
|
||||||
|
isAuthenticated: true
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return state
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const formData = (state = initial.formData, action) => {
|
||||||
|
switch (action.type) {
|
||||||
|
case 'UPDATE_FORMDATA_EMAIL':
|
||||||
|
return {
|
||||||
|
formData: {
|
||||||
|
...state.formData,
|
||||||
|
email: action.email
|
||||||
|
},
|
||||||
|
}
|
||||||
|
case 'UPDATE_FORMDATA_USERNAME':
|
||||||
|
return {
|
||||||
|
formData: {
|
||||||
|
...state.formData,
|
||||||
|
username: action.username
|
||||||
|
},
|
||||||
|
}
|
||||||
|
case 'UPDATE_FORMDATA_PASSWORD':
|
||||||
|
return {
|
||||||
|
formData: {
|
||||||
|
...state.formData,
|
||||||
|
password: action.password
|
||||||
|
},
|
||||||
|
}
|
||||||
|
case 'PROFILE_SUCCESS':
|
||||||
|
return initial.formData
|
||||||
|
default:
|
||||||
|
return state
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const reducers = combineReducers({
|
const reducers = combineReducers({
|
||||||
|
message,
|
||||||
user,
|
user,
|
||||||
|
formData,
|
||||||
})
|
})
|
||||||
|
|
||||||
export default reducers
|
export default reducers
|
||||||
|
18
mpwo_client/src/reducers/initial.js
Normal file
18
mpwo_client/src/reducers/initial.js
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
export default {
|
||||||
|
message: '',
|
||||||
|
user: {
|
||||||
|
id: '',
|
||||||
|
username: '',
|
||||||
|
email: '',
|
||||||
|
createdAt: '',
|
||||||
|
isAdmin: false,
|
||||||
|
isAuthenticated: false,
|
||||||
|
},
|
||||||
|
formData: {
|
||||||
|
formData: {
|
||||||
|
username: '',
|
||||||
|
email: '',
|
||||||
|
password: ''
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
@ -1,21 +0,0 @@
|
|||||||
const user = (state = null, action) => {
|
|
||||||
switch (action.type) {
|
|
||||||
case 'AUTH_ERROR':
|
|
||||||
case 'PROFILE_ERROR':
|
|
||||||
case 'LOGOUT':
|
|
||||||
window.localStorage.removeItem('authToken')
|
|
||||||
return null
|
|
||||||
case 'PROFILE_SUCCESS':
|
|
||||||
return {
|
|
||||||
id: action.message.data.id,
|
|
||||||
username: action.message.data.username,
|
|
||||||
email: action.message.data.email,
|
|
||||||
isAdmin: action.message.data.is_admin,
|
|
||||||
createdAt: action.message.data.created_at,
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
return state
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default user
|
|
Loading…
Reference in New Issue
Block a user