API & Client: registration controls
This commit is contained in:
parent
62d87d05f5
commit
cac4f368bf
3
Makefile
3
Makefile
@ -14,3 +14,6 @@ serve-react:
|
|||||||
|
|
||||||
serve:
|
serve:
|
||||||
$(MAKE) P="serve-react serve-python" make-p
|
$(MAKE) P="serve-react serve-python" make-p
|
||||||
|
|
||||||
|
test-python:
|
||||||
|
$(FLASK) test
|
||||||
|
@ -14,7 +14,8 @@ class TestAuthBlueprint(BaseTestCase):
|
|||||||
data=json.dumps(dict(
|
data=json.dumps(dict(
|
||||||
username='justatest',
|
username='justatest',
|
||||||
email='test@test.com',
|
email='test@test.com',
|
||||||
password='123456'
|
password='12345678',
|
||||||
|
password_conf='12345678'
|
||||||
)),
|
)),
|
||||||
content_type='application/json'
|
content_type='application/json'
|
||||||
)
|
)
|
||||||
@ -26,14 +27,15 @@ class TestAuthBlueprint(BaseTestCase):
|
|||||||
self.assertEqual(response.status_code, 201)
|
self.assertEqual(response.status_code, 201)
|
||||||
|
|
||||||
def test_user_registration_user_already_exists(self):
|
def test_user_registration_user_already_exists(self):
|
||||||
add_user('test', 'test@test.com', 'test')
|
add_user('test', 'test@test.com', '12345678')
|
||||||
with self.client:
|
with self.client:
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
'/api/auth/register',
|
'/api/auth/register',
|
||||||
data=json.dumps(dict(
|
data=json.dumps(dict(
|
||||||
username='test',
|
username='test',
|
||||||
email='test@test.com',
|
email='test@test.com',
|
||||||
password='test'
|
password='12345678',
|
||||||
|
password_conf='12345678'
|
||||||
)),
|
)),
|
||||||
content_type='application/json'
|
content_type='application/json'
|
||||||
)
|
)
|
||||||
@ -44,6 +46,101 @@ class TestAuthBlueprint(BaseTestCase):
|
|||||||
self.assertTrue(response.content_type == 'application/json')
|
self.assertTrue(response.content_type == 'application/json')
|
||||||
self.assertEqual(response.status_code, 400)
|
self.assertEqual(response.status_code, 400)
|
||||||
|
|
||||||
|
def test_user_registration_invalid_short_username(self):
|
||||||
|
with self.client:
|
||||||
|
response = self.client.post(
|
||||||
|
'/api/auth/register',
|
||||||
|
data=json.dumps(dict(
|
||||||
|
username='t',
|
||||||
|
email='test@test.com',
|
||||||
|
password='12345678',
|
||||||
|
password_conf='12345678'
|
||||||
|
)),
|
||||||
|
content_type='application/json'
|
||||||
|
)
|
||||||
|
data = json.loads(response.data.decode())
|
||||||
|
self.assertTrue(data['status'] == 'error')
|
||||||
|
self.assertTrue(
|
||||||
|
data['message'] == "Errors: Username: 3 to 12 characters required.\n")
|
||||||
|
self.assertTrue(response.content_type == 'application/json')
|
||||||
|
self.assertEqual(response.status_code, 400)
|
||||||
|
|
||||||
|
def test_user_registration_invalid_long_username(self):
|
||||||
|
with self.client:
|
||||||
|
response = self.client.post(
|
||||||
|
'/api/auth/register',
|
||||||
|
data=json.dumps(dict(
|
||||||
|
username='testestestestestest',
|
||||||
|
email='test@test.com',
|
||||||
|
password='12345678',
|
||||||
|
password_conf='12345678'
|
||||||
|
)),
|
||||||
|
content_type='application/json'
|
||||||
|
)
|
||||||
|
data = json.loads(response.data.decode())
|
||||||
|
self.assertTrue(data['status'] == 'error')
|
||||||
|
self.assertTrue(
|
||||||
|
data['message'] == "Errors: Username: 3 to 12 characters required.\n")
|
||||||
|
self.assertTrue(response.content_type == 'application/json')
|
||||||
|
self.assertEqual(response.status_code, 400)
|
||||||
|
|
||||||
|
def test_user_registration_invalid_email(self):
|
||||||
|
with self.client:
|
||||||
|
response = self.client.post(
|
||||||
|
'/api/auth/register',
|
||||||
|
data=json.dumps(dict(
|
||||||
|
username='test',
|
||||||
|
email='test@test',
|
||||||
|
password='12345678',
|
||||||
|
password_conf='12345678'
|
||||||
|
)),
|
||||||
|
content_type='application/json'
|
||||||
|
)
|
||||||
|
data = json.loads(response.data.decode())
|
||||||
|
self.assertTrue(data['status'] == 'error')
|
||||||
|
self.assertTrue(
|
||||||
|
data['message'] == "Errors: Valid email must be provided.\n")
|
||||||
|
self.assertTrue(response.content_type == 'application/json')
|
||||||
|
self.assertEqual(response.status_code, 400)
|
||||||
|
|
||||||
|
def test_user_registration_invalid_short_password(self):
|
||||||
|
with self.client:
|
||||||
|
response = self.client.post(
|
||||||
|
'/api/auth/register',
|
||||||
|
data=json.dumps(dict(
|
||||||
|
username='test',
|
||||||
|
email='test@test.com',
|
||||||
|
password='1234567',
|
||||||
|
password_conf='1234567'
|
||||||
|
)),
|
||||||
|
content_type='application/json'
|
||||||
|
)
|
||||||
|
data = json.loads(response.data.decode())
|
||||||
|
self.assertTrue(data['status'] == 'error')
|
||||||
|
self.assertTrue(
|
||||||
|
data['message'] == "Errors: Password: 8 characters required.\n")
|
||||||
|
self.assertTrue(response.content_type == 'application/json')
|
||||||
|
self.assertEqual(response.status_code, 400)
|
||||||
|
|
||||||
|
def test_user_registration_mismatched_password(self):
|
||||||
|
with self.client:
|
||||||
|
response = self.client.post(
|
||||||
|
'/api/auth/register',
|
||||||
|
data=json.dumps(dict(
|
||||||
|
username='test',
|
||||||
|
email='test@test.com',
|
||||||
|
password='12345678',
|
||||||
|
password_conf='87654321'
|
||||||
|
)),
|
||||||
|
content_type='application/json'
|
||||||
|
)
|
||||||
|
data = json.loads(response.data.decode())
|
||||||
|
self.assertTrue(data['status'] == 'error')
|
||||||
|
self.assertTrue(
|
||||||
|
data['message'] == "Errors: Password and password confirmation don\'t match.\n")
|
||||||
|
self.assertTrue(response.content_type == 'application/json')
|
||||||
|
self.assertEqual(response.status_code, 400)
|
||||||
|
|
||||||
def test_user_registration_invalid_json(self):
|
def test_user_registration_invalid_json(self):
|
||||||
with self.client:
|
with self.client:
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
@ -60,7 +157,10 @@ class TestAuthBlueprint(BaseTestCase):
|
|||||||
with self.client:
|
with self.client:
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
'/api/auth/register',
|
'/api/auth/register',
|
||||||
data=json.dumps(dict(email='test@test.com', password='test')),
|
data=json.dumps(dict(
|
||||||
|
email='test@test.com',
|
||||||
|
password='12345678',
|
||||||
|
password_conf='12345678')),
|
||||||
content_type='application/json',
|
content_type='application/json',
|
||||||
)
|
)
|
||||||
data = json.loads(response.data.decode())
|
data = json.loads(response.data.decode())
|
||||||
@ -73,7 +173,9 @@ class TestAuthBlueprint(BaseTestCase):
|
|||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
'/api/auth/register',
|
'/api/auth/register',
|
||||||
data=json.dumps(dict(
|
data=json.dumps(dict(
|
||||||
username='test', password='test')),
|
username='test',
|
||||||
|
password='12345678',
|
||||||
|
password_conf='12345678')),
|
||||||
content_type='application/json',
|
content_type='application/json',
|
||||||
)
|
)
|
||||||
data = json.loads(response.data.decode())
|
data = json.loads(response.data.decode())
|
||||||
@ -86,7 +188,24 @@ class TestAuthBlueprint(BaseTestCase):
|
|||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
'/api/auth/register',
|
'/api/auth/register',
|
||||||
data=json.dumps(dict(
|
data=json.dumps(dict(
|
||||||
username='test', email='test@test.com')),
|
username='test',
|
||||||
|
email='test@test.com',
|
||||||
|
password_conf='12345678')),
|
||||||
|
content_type='application/json',
|
||||||
|
)
|
||||||
|
data = json.loads(response.data.decode())
|
||||||
|
self.assertEqual(response.status_code, 400)
|
||||||
|
self.assertIn('Invalid payload.', data['message'])
|
||||||
|
self.assertIn('error', data['status'])
|
||||||
|
|
||||||
|
def test_user_registration_invalid_json_keys_no_password_conf(self):
|
||||||
|
with self.client:
|
||||||
|
response = self.client.post(
|
||||||
|
'/api/auth/register',
|
||||||
|
data=json.dumps(dict(
|
||||||
|
username='test',
|
||||||
|
email='test@test.com',
|
||||||
|
password='12345678')),
|
||||||
content_type='application/json',
|
content_type='application/json',
|
||||||
)
|
)
|
||||||
data = json.loads(response.data.decode())
|
data = json.loads(response.data.decode())
|
||||||
@ -96,12 +215,12 @@ class TestAuthBlueprint(BaseTestCase):
|
|||||||
|
|
||||||
def test_registered_user_login(self):
|
def test_registered_user_login(self):
|
||||||
with self.client:
|
with self.client:
|
||||||
add_user('test', 'test@test.com', 'test')
|
add_user('test', 'test@test.com', '12345678')
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
'/api/auth/login',
|
'/api/auth/login',
|
||||||
data=json.dumps(dict(
|
data=json.dumps(dict(
|
||||||
email='test@test.com',
|
email='test@test.com',
|
||||||
password='test'
|
password='12345678'
|
||||||
)),
|
)),
|
||||||
content_type='application/json'
|
content_type='application/json'
|
||||||
)
|
)
|
||||||
@ -118,25 +237,42 @@ class TestAuthBlueprint(BaseTestCase):
|
|||||||
'/api/auth/login',
|
'/api/auth/login',
|
||||||
data=json.dumps(dict(
|
data=json.dumps(dict(
|
||||||
email='test@test.com',
|
email='test@test.com',
|
||||||
password='test'
|
password='12345678'
|
||||||
)),
|
)),
|
||||||
content_type='application/json'
|
content_type='application/json'
|
||||||
)
|
)
|
||||||
data = json.loads(response.data.decode())
|
data = json.loads(response.data.decode())
|
||||||
self.assertTrue(data['status'] == 'error')
|
self.assertTrue(data['status'] == 'error')
|
||||||
self.assertTrue(data['message'] == 'User does not exist.')
|
self.assertTrue(data['message'] == 'Invalid credentials.')
|
||||||
|
self.assertTrue(response.content_type == 'application/json')
|
||||||
|
self.assertEqual(response.status_code, 404)
|
||||||
|
|
||||||
|
def test_registered_user_login_invalid_password(self):
|
||||||
|
add_user('test', 'test@test.com', '12345678')
|
||||||
|
with self.client:
|
||||||
|
response = self.client.post(
|
||||||
|
'/api/auth/login',
|
||||||
|
data=json.dumps(dict(
|
||||||
|
email='test@test.com',
|
||||||
|
password='123456789'
|
||||||
|
)),
|
||||||
|
content_type='application/json'
|
||||||
|
)
|
||||||
|
data = json.loads(response.data.decode())
|
||||||
|
self.assertTrue(data['status'] == 'error')
|
||||||
|
self.assertTrue(data['message'] == 'Invalid credentials.')
|
||||||
self.assertTrue(response.content_type == 'application/json')
|
self.assertTrue(response.content_type == 'application/json')
|
||||||
self.assertEqual(response.status_code, 404)
|
self.assertEqual(response.status_code, 404)
|
||||||
|
|
||||||
def test_valid_logout(self):
|
def test_valid_logout(self):
|
||||||
add_user('test', 'test@test.com', 'test')
|
add_user('test', 'test@test.com', '12345678')
|
||||||
with self.client:
|
with self.client:
|
||||||
# user login
|
# user login
|
||||||
resp_login = self.client.post(
|
resp_login = self.client.post(
|
||||||
'/api/auth/login',
|
'/api/auth/login',
|
||||||
data=json.dumps(dict(
|
data=json.dumps(dict(
|
||||||
email='test@test.com',
|
email='test@test.com',
|
||||||
password='test'
|
password='12345678'
|
||||||
)),
|
)),
|
||||||
content_type='application/json'
|
content_type='application/json'
|
||||||
)
|
)
|
||||||
@ -155,13 +291,13 @@ class TestAuthBlueprint(BaseTestCase):
|
|||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
def test_invalid_logout_expired_token(self):
|
def test_invalid_logout_expired_token(self):
|
||||||
add_user('test', 'test@test.com', 'test')
|
add_user('test', 'test@test.com', '12345678')
|
||||||
with self.client:
|
with self.client:
|
||||||
resp_login = self.client.post(
|
resp_login = self.client.post(
|
||||||
'/api/auth/login',
|
'/api/auth/login',
|
||||||
data=json.dumps(dict(
|
data=json.dumps(dict(
|
||||||
email='test@test.com',
|
email='test@test.com',
|
||||||
password='test'
|
password='12345678'
|
||||||
)),
|
)),
|
||||||
content_type='application/json'
|
content_type='application/json'
|
||||||
)
|
)
|
||||||
|
@ -4,7 +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
|
from .utils import authenticate, register_controls
|
||||||
|
|
||||||
auth_blueprint = Blueprint('auth', __name__)
|
auth_blueprint = Blueprint('auth', __name__)
|
||||||
|
|
||||||
@ -15,7 +15,8 @@ def register_user():
|
|||||||
post_data = request.get_json()
|
post_data = request.get_json()
|
||||||
if not post_data or post_data.get('username') is None \
|
if not post_data or post_data.get('username') is None \
|
||||||
or post_data.get('email') is None \
|
or post_data.get('email') is None \
|
||||||
or post_data.get('password') is None:
|
or post_data.get('password') is None \
|
||||||
|
or post_data.get('password_conf') is None:
|
||||||
response_object = {
|
response_object = {
|
||||||
'status': 'error',
|
'status': 'error',
|
||||||
'message': 'Invalid payload.'
|
'message': 'Invalid payload.'
|
||||||
@ -24,6 +25,16 @@ def register_user():
|
|||||||
username = post_data.get('username')
|
username = post_data.get('username')
|
||||||
email = post_data.get('email')
|
email = post_data.get('email')
|
||||||
password = post_data.get('password')
|
password = post_data.get('password')
|
||||||
|
password_conf = post_data.get('password_conf')
|
||||||
|
|
||||||
|
ret = register_controls(username, email, password, password_conf)
|
||||||
|
if ret != '':
|
||||||
|
response_object = {
|
||||||
|
'status': 'error',
|
||||||
|
'message': 'Errors: ' + ret
|
||||||
|
}
|
||||||
|
return jsonify(response_object), 400
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# check for existing user
|
# check for existing user
|
||||||
user = User.query.filter(
|
user = User.query.filter(
|
||||||
@ -90,7 +101,7 @@ def login_user():
|
|||||||
else:
|
else:
|
||||||
response_object = {
|
response_object = {
|
||||||
'status': 'error',
|
'status': 'error',
|
||||||
'message': 'User does not exist.'
|
'message': 'Invalid credentials.'
|
||||||
}
|
}
|
||||||
return jsonify(response_object), 404
|
return jsonify(response_object), 404
|
||||||
# handler errors
|
# handler errors
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
from functools import wraps
|
from functools import wraps
|
||||||
|
import re
|
||||||
|
|
||||||
from flask import request, jsonify
|
from flask import request, jsonify
|
||||||
|
|
||||||
@ -33,3 +34,21 @@ def authenticate(f):
|
|||||||
def is_admin(user_id):
|
def is_admin(user_id):
|
||||||
user = User.query.filter_by(id=user_id).first()
|
user = User.query.filter_by(id=user_id).first()
|
||||||
return user.admin
|
return user.admin
|
||||||
|
|
||||||
|
|
||||||
|
def is_valid_email(email):
|
||||||
|
mail_pattern = r"(^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$)"
|
||||||
|
return re.match(mail_pattern, email) is not None
|
||||||
|
|
||||||
|
|
||||||
|
def register_controls(username, email, password, password_conf):
|
||||||
|
ret = ''
|
||||||
|
if not 2 < len(username) < 13:
|
||||||
|
ret += 'Username: 3 to 12 characters required.\n'
|
||||||
|
if not is_valid_email(email):
|
||||||
|
ret += 'Valid email must be provided.\n'
|
||||||
|
if password != password_conf:
|
||||||
|
ret += 'Password and password confirmation don\'t match.\n'
|
||||||
|
if len(password) < 8:
|
||||||
|
ret += 'Password: 8 characters required.\n'
|
||||||
|
return ret
|
||||||
|
@ -26,7 +26,7 @@ def seed_db():
|
|||||||
def test():
|
def test():
|
||||||
"""Runs the tests without code coverage."""
|
"""Runs the tests without code coverage."""
|
||||||
tests = unittest.TestLoader().discover(
|
tests = unittest.TestLoader().discover(
|
||||||
'mpwo_api/tests', pattern='test*.py')
|
'mpwo_api/mpwo_api/tests', pattern='test*.py')
|
||||||
result = unittest.TextTestRunner(verbosity=2).run(tests)
|
result = unittest.TextTestRunner(verbosity=2).run(tests)
|
||||||
if result.wasSuccessful():
|
if result.wasSuccessful():
|
||||||
return 0
|
return 0
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
"react": "^16.2.0",
|
"react": "^16.2.0",
|
||||||
"react-dom": "^16.2.0",
|
"react-dom": "^16.2.0",
|
||||||
"react-helmet": "^5.2.0",
|
"react-helmet": "^5.2.0",
|
||||||
|
"react-key-index": "^0.1.1",
|
||||||
"react-redux": "^5.0.6",
|
"react-redux": "^5.0.6",
|
||||||
"react-router-dom": "^4.2.2",
|
"react-router-dom": "^4.2.2",
|
||||||
"react-router-redux": "^5.0.0-alpha.9",
|
"react-router-redux": "^5.0.0-alpha.9",
|
||||||
|
@ -1,9 +1,14 @@
|
|||||||
|
import keyIndex from 'react-key-index'
|
||||||
import mpwoApi from '../mpwoApi'
|
import mpwoApi from '../mpwoApi'
|
||||||
|
|
||||||
function AuthError(message) {
|
function AuthError(message) {
|
||||||
return { type: 'AUTH_ERROR', message }
|
return { type: 'AUTH_ERROR', message }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function AuthErrors(messages) {
|
||||||
|
return { type: 'AUTH_ERRORS', messages }
|
||||||
|
}
|
||||||
|
|
||||||
function ProfileSuccess(message) {
|
function ProfileSuccess(message) {
|
||||||
return { type: 'PROFILE_SUCCESS', message }
|
return { type: 'PROFILE_SUCCESS', message }
|
||||||
}
|
}
|
||||||
@ -27,6 +32,11 @@ const updateFormDataPassword = value => ({
|
|||||||
password: value,
|
password: value,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const updateFormDataPasswordConf = value => ({
|
||||||
|
type: 'UPDATE_FORMDATA_PASSWORD_CONF',
|
||||||
|
passwordConf: value,
|
||||||
|
})
|
||||||
|
|
||||||
export function getProfile(dispatch) {
|
export function getProfile(dispatch) {
|
||||||
return mpwoApi
|
return mpwoApi
|
||||||
.getProfile()
|
.getProfile()
|
||||||
@ -46,7 +56,11 @@ export function getProfile(dispatch) {
|
|||||||
export function register(formData) {
|
export function register(formData) {
|
||||||
return function(dispatch) {
|
return function(dispatch) {
|
||||||
return mpwoApi
|
return mpwoApi
|
||||||
.register(formData.username, formData.email, formData.password)
|
.register(
|
||||||
|
formData.username,
|
||||||
|
formData.email,
|
||||||
|
formData.password,
|
||||||
|
formData.passwordConf)
|
||||||
.then(ret => {
|
.then(ret => {
|
||||||
if (ret.status === 'success') {
|
if (ret.status === 'success') {
|
||||||
window.localStorage.setItem('authToken', ret.auth_token)
|
window.localStorage.setItem('authToken', ret.auth_token)
|
||||||
@ -92,6 +106,20 @@ export function logout() {
|
|||||||
return { type: 'LOGOUT' }
|
return { type: 'LOGOUT' }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function RegisterFormControl (formData) {
|
||||||
|
const errMsg = []
|
||||||
|
if (formData.username.length < 3 || formData.username.length > 12) {
|
||||||
|
errMsg.push('Username: 3 to 12 characters required.')
|
||||||
|
}
|
||||||
|
if (formData.password !== formData.passwordConf) {
|
||||||
|
errMsg.push('Password and password confirmation don\'t match.')
|
||||||
|
}
|
||||||
|
if (formData.password.length < 8) {
|
||||||
|
errMsg.push('Password: 8 characters required.')
|
||||||
|
}
|
||||||
|
return errMsg
|
||||||
|
}
|
||||||
|
|
||||||
export function handleUserFormSubmit(event, formType) {
|
export function handleUserFormSubmit(event, formType) {
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
return (dispatch, getState) => {
|
return (dispatch, getState) => {
|
||||||
@ -101,7 +129,12 @@ export function handleUserFormSubmit(event, formType) {
|
|||||||
if (formType === 'Login') {
|
if (formType === 'Login') {
|
||||||
dispatch(login(formData.formData))
|
dispatch(login(formData.formData))
|
||||||
} else { // formType === 'Register'
|
} else { // formType === 'Register'
|
||||||
dispatch(register(formData.formData))
|
const ret = RegisterFormControl(formData.formData)
|
||||||
|
if (ret.length === 0) {
|
||||||
|
dispatch(register(formData.formData))
|
||||||
|
} else {
|
||||||
|
dispatch(AuthErrors(keyIndex(ret, 1)))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -112,7 +145,9 @@ export const handleFormChange = event => dispatch => {
|
|||||||
return dispatch(updateFormDataEmail(event.target.value))
|
return dispatch(updateFormDataEmail(event.target.value))
|
||||||
case 'username':
|
case 'username':
|
||||||
return dispatch(updateFormDataUsername(event.target.value))
|
return dispatch(updateFormDataUsername(event.target.value))
|
||||||
default: // case 'password':
|
case 'password':
|
||||||
return dispatch(updateFormDataPassword(event.target.value))
|
return dispatch(updateFormDataPassword(event.target.value))
|
||||||
|
default: // case 'password-conf':
|
||||||
|
return dispatch(updateFormDataPasswordConf(event.target.value))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
.App {
|
.App {
|
||||||
/*text-align: center;*/
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.App-logo {
|
.App-logo {
|
||||||
@ -27,6 +27,10 @@
|
|||||||
to { transform: rotate(360deg); }
|
to { transform: rotate(360deg); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.card {
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
.page-title {
|
.page-title {
|
||||||
font-size: 2em;
|
font-size: 2em;
|
||||||
margin: 1em;
|
margin: 1em;
|
||||||
|
@ -1,10 +1,13 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
import { Helmet } from 'react-helmet'
|
||||||
|
|
||||||
export default function Form (props) {
|
export default function Form (props) {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<h1>{props.formType}</h1>
|
<Helmet>
|
||||||
|
<title>mpwo - {props.formType}</title>
|
||||||
|
</Helmet>
|
||||||
|
<h1 className="page-title">{props.formType}</h1>
|
||||||
<div className="container">
|
<div className="container">
|
||||||
<div className="row">
|
<div className="row">
|
||||||
<div className="col-md-3" />
|
<div className="col-md-3" />
|
||||||
@ -13,7 +16,7 @@ export default function Form (props) {
|
|||||||
<form onSubmit={event =>
|
<form onSubmit={event =>
|
||||||
props.handleUserFormSubmit(event, props.formType)}
|
props.handleUserFormSubmit(event, props.formType)}
|
||||||
>
|
>
|
||||||
{props.formType === 'Register' &&
|
{props.formType === 'Register' &&
|
||||||
<div className="form-group">
|
<div className="form-group">
|
||||||
<input
|
<input
|
||||||
name="username"
|
name="username"
|
||||||
@ -24,16 +27,16 @@ export default function Form (props) {
|
|||||||
onChange={props.onHandleFormChange}
|
onChange={props.onHandleFormChange}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
<div className="form-group">
|
<div className="form-group">
|
||||||
<input
|
<input
|
||||||
name="email"
|
name="email"
|
||||||
className="form-control input-lg"
|
className="form-control input-lg"
|
||||||
type="email"
|
type="email"
|
||||||
placeholder="Enter an email address"
|
placeholder="Enter an email address"
|
||||||
required
|
required
|
||||||
onChange={props.onHandleFormChange}
|
onChange={props.onHandleFormChange}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="form-group">
|
<div className="form-group">
|
||||||
<input
|
<input
|
||||||
@ -45,6 +48,18 @@ export default function Form (props) {
|
|||||||
onChange={props.onHandleFormChange}
|
onChange={props.onHandleFormChange}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
{props.formType === 'Register' &&
|
||||||
|
<div className="form-group">
|
||||||
|
<input
|
||||||
|
name="password-conf"
|
||||||
|
className="form-control input-lg"
|
||||||
|
type="password"
|
||||||
|
placeholder="Enter password confirmation"
|
||||||
|
required
|
||||||
|
onChange={props.onHandleFormChange}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
<input
|
<input
|
||||||
type="submit"
|
type="submit"
|
||||||
className="btn btn-primary btn-lg btn-block"
|
className="btn btn-primary btn-lg btn-block"
|
||||||
|
@ -11,7 +11,7 @@ class Logout extends React.Component {
|
|||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<p>
|
<p className="App-center">
|
||||||
You are now logged out.
|
You are now logged out.
|
||||||
Click <Link to="/login">here</Link> to log back in.</p>
|
Click <Link to="/login">here</Link> to log back in.</p>
|
||||||
</div>
|
</div>
|
||||||
|
@ -16,6 +16,17 @@ function UserForm(props) {
|
|||||||
{ props.message !== '' && (
|
{ props.message !== '' && (
|
||||||
<code>{props.message}</code>
|
<code>{props.message}</code>
|
||||||
)}
|
)}
|
||||||
|
{ props.messages.length > 0 && (
|
||||||
|
<code>
|
||||||
|
<ul>
|
||||||
|
{props.messages.map(msg => (
|
||||||
|
<li key={msg.id}>
|
||||||
|
{msg.value}
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</code>
|
||||||
|
)}
|
||||||
<Form
|
<Form
|
||||||
formType={props.formType}
|
formType={props.formType}
|
||||||
userForm={props.formData}
|
userForm={props.formData}
|
||||||
@ -32,6 +43,7 @@ export default connect(
|
|||||||
state => ({
|
state => ({
|
||||||
formData: state.formData,
|
formData: state.formData,
|
||||||
message: state.message,
|
message: state.message,
|
||||||
|
messages: state.messages,
|
||||||
}),
|
}),
|
||||||
dispatch => ({
|
dispatch => ({
|
||||||
onHandleFormChange: event => {
|
onHandleFormChange: event => {
|
||||||
|
@ -17,7 +17,7 @@ export default class MpwoApi {
|
|||||||
.then(response => response.json())
|
.then(response => response.json())
|
||||||
.catch(error => error)
|
.catch(error => error)
|
||||||
}
|
}
|
||||||
static register(username, email, password) {
|
static register(username, email, password, passwordConf) {
|
||||||
const request = new Request(`${apiUrl}auth/register`, {
|
const request = new Request(`${apiUrl}auth/register`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: new Headers({
|
headers: new Headers({
|
||||||
@ -27,6 +27,7 @@ export default class MpwoApi {
|
|||||||
username: username,
|
username: username,
|
||||||
email: email,
|
email: email,
|
||||||
password: password,
|
password: password,
|
||||||
|
password_conf: passwordConf,
|
||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
return fetch(request)
|
return fetch(request)
|
||||||
|
@ -2,20 +2,70 @@ import { combineReducers } from 'redux'
|
|||||||
|
|
||||||
import initial from './initial'
|
import initial from './initial'
|
||||||
|
|
||||||
|
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 'UPDATE_FORMDATA_PASSWORD_CONF':
|
||||||
|
return {
|
||||||
|
formData: {
|
||||||
|
...state.formData,
|
||||||
|
passwordConf: action.passwordConf
|
||||||
|
},
|
||||||
|
}
|
||||||
|
case 'PROFILE_SUCCESS':
|
||||||
|
return initial.formData
|
||||||
|
default:
|
||||||
|
return state
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const message = (state = initial.message, action) => {
|
const message = (state = initial.message, action) => {
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
case 'AUTH_ERROR':
|
case 'AUTH_ERROR':
|
||||||
case 'PROFILE_ERROR':
|
case 'PROFILE_ERROR':
|
||||||
return action.message
|
return action.message
|
||||||
case 'LOGOUT':
|
case 'LOGOUT':
|
||||||
return ''
|
|
||||||
case 'PROFILE_SUCCESS':
|
case 'PROFILE_SUCCESS':
|
||||||
|
case '@@router/LOCATION_CHANGE':
|
||||||
return ''
|
return ''
|
||||||
default:
|
default:
|
||||||
return state
|
return state
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const messages = (state = initial.messages, action) => {
|
||||||
|
switch (action.type) {
|
||||||
|
case 'AUTH_ERRORS':
|
||||||
|
return action.messages
|
||||||
|
case 'LOGOUT':
|
||||||
|
case 'PROFILE_SUCCESS':
|
||||||
|
case '@@router/LOCATION_CHANGE':
|
||||||
|
return []
|
||||||
|
default:
|
||||||
|
return state
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const user = (state = initial.user, action) => {
|
const user = (state = initial.user, action) => {
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
case 'AUTH_ERROR':
|
case 'AUTH_ERROR':
|
||||||
@ -37,40 +87,11 @@ const user = (state = initial.user, action) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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,
|
|
||||||
formData,
|
formData,
|
||||||
|
message,
|
||||||
|
messages,
|
||||||
|
user,
|
||||||
})
|
})
|
||||||
|
|
||||||
export default reducers
|
export default reducers
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
export default {
|
export default {
|
||||||
message: '',
|
message: '',
|
||||||
|
messages: [],
|
||||||
user: {
|
user: {
|
||||||
id: '',
|
id: '',
|
||||||
username: '',
|
username: '',
|
||||||
@ -12,7 +13,8 @@ export default {
|
|||||||
formData: {
|
formData: {
|
||||||
username: '',
|
username: '',
|
||||||
email: '',
|
email: '',
|
||||||
password: ''
|
password: '',
|
||||||
|
passwordConf: '',
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
16
package-lock.json
generated
16
package-lock.json
generated
@ -4059,8 +4059,7 @@
|
|||||||
},
|
},
|
||||||
"jsbn": {
|
"jsbn": {
|
||||||
"version": "0.1.1",
|
"version": "0.1.1",
|
||||||
"bundled": true,
|
"bundled": true
|
||||||
"optional": true
|
|
||||||
},
|
},
|
||||||
"json-schema": {
|
"json-schema": {
|
||||||
"version": "0.2.3",
|
"version": "0.2.3",
|
||||||
@ -4726,6 +4725,11 @@
|
|||||||
"minimalistic-assert": "1.0.0"
|
"minimalistic-assert": "1.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"hashids": {
|
||||||
|
"version": "1.1.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/hashids/-/hashids-1.1.4.tgz",
|
||||||
|
"integrity": "sha512-U/fnTE3edW0AV92ZI/BfEluMZuVcu3MDOopsN7jS+HqDYcarQo8rXQiWlsBlm0uX48/taYSdxRsfzh2HRg5Z6w=="
|
||||||
|
},
|
||||||
"hawk": {
|
"hawk": {
|
||||||
"version": "6.0.2",
|
"version": "6.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/hawk/-/hawk-6.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/hawk/-/hawk-6.0.2.tgz",
|
||||||
@ -9311,6 +9315,14 @@
|
|||||||
"react-side-effect": "1.1.3"
|
"react-side-effect": "1.1.3"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"react-key-index": {
|
||||||
|
"version": "0.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-key-index/-/react-key-index-0.1.1.tgz",
|
||||||
|
"integrity": "sha1-gxnk8JYa5EqOsKT3bkwhDvbTnNo=",
|
||||||
|
"requires": {
|
||||||
|
"hashids": "1.1.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
"react-redux": {
|
"react-redux": {
|
||||||
"version": "5.0.6",
|
"version": "5.0.6",
|
||||||
"resolved": "https://registry.npmjs.org/react-redux/-/react-redux-5.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/react-redux/-/react-redux-5.0.6.tgz",
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
"react": "^16.2.0",
|
"react": "^16.2.0",
|
||||||
"react-dom": "^16.2.0",
|
"react-dom": "^16.2.0",
|
||||||
"react-helmet": "^5.2.0",
|
"react-helmet": "^5.2.0",
|
||||||
|
"react-key-index": "^0.1.1",
|
||||||
"react-redux": "^5.0.6",
|
"react-redux": "^5.0.6",
|
||||||
"react-router-dom": "^4.2.2",
|
"react-router-dom": "^4.2.2",
|
||||||
"react-router-redux": "^5.0.0-alpha.9",
|
"react-router-redux": "^5.0.0-alpha.9",
|
||||||
|
Loading…
Reference in New Issue
Block a user