API & Client - resend account confirmation email
This commit is contained in:
parent
decff1cd6a
commit
612549ddea
3
fittrackee/tests/fixtures/fixtures_users.py
vendored
3
fittrackee/tests/fixtures/fixtures_users.py
vendored
@ -6,6 +6,8 @@ from fittrackee import db
|
|||||||
from fittrackee.users.models import User, UserSportPreference
|
from fittrackee.users.models import User, UserSportPreference
|
||||||
from fittrackee.workouts.models import Sport
|
from fittrackee.workouts.models import Sport
|
||||||
|
|
||||||
|
from ..utils import random_string
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture()
|
@pytest.fixture()
|
||||||
def user_1() -> User:
|
def user_1() -> User:
|
||||||
@ -97,6 +99,7 @@ def inactive_user() -> User:
|
|||||||
user = User(
|
user = User(
|
||||||
username='inactive', email='inactive@example.com', password='12345678'
|
username='inactive', email='inactive@example.com', password='12345678'
|
||||||
)
|
)
|
||||||
|
user.confirmation_token = random_string()
|
||||||
db.session.add(user)
|
db.session.add(user)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
return user
|
return user
|
||||||
|
@ -2134,3 +2134,129 @@ class TestConfirmationAccount(ApiTestCaseMixin):
|
|||||||
assert data['message'] == 'account confirmation successful'
|
assert data['message'] == 'account confirmation successful'
|
||||||
assert inactive_user.is_active is True
|
assert inactive_user.is_active is True
|
||||||
assert inactive_user.confirmation_token is None
|
assert inactive_user.confirmation_token is None
|
||||||
|
|
||||||
|
|
||||||
|
class TestResendAccountConfirmationEmail(ApiTestCaseMixin):
|
||||||
|
def test_it_returns_error_if_email_is_missing(self, app: Flask) -> None:
|
||||||
|
client = app.test_client()
|
||||||
|
|
||||||
|
response = client.post(
|
||||||
|
'/api/auth/account/resend-confirmation',
|
||||||
|
data=json.dumps(dict()),
|
||||||
|
content_type='application/json',
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assert_400(response)
|
||||||
|
|
||||||
|
def test_it_does_not_return_error_if_account_does_not_exist(
|
||||||
|
self, app: Flask
|
||||||
|
) -> None:
|
||||||
|
client = app.test_client()
|
||||||
|
|
||||||
|
response = client.post(
|
||||||
|
'/api/auth/account/resend-confirmation',
|
||||||
|
data=json.dumps(dict(email=self.random_email())),
|
||||||
|
content_type='application/json',
|
||||||
|
)
|
||||||
|
|
||||||
|
assert response.status_code == 200
|
||||||
|
data = json.loads(response.data.decode())
|
||||||
|
assert data['status'] == 'success'
|
||||||
|
assert data['message'] == 'confirmation email resent'
|
||||||
|
|
||||||
|
def test_it_does_not_return_error_if_account_already_active(
|
||||||
|
self, app: Flask, user_1: User
|
||||||
|
) -> None:
|
||||||
|
client = app.test_client()
|
||||||
|
|
||||||
|
response = client.post(
|
||||||
|
'/api/auth/account/resend-confirmation',
|
||||||
|
data=json.dumps(dict(email=user_1.email)),
|
||||||
|
content_type='application/json',
|
||||||
|
)
|
||||||
|
|
||||||
|
assert response.status_code == 200
|
||||||
|
data = json.loads(response.data.decode())
|
||||||
|
assert data['status'] == 'success'
|
||||||
|
assert data['message'] == 'confirmation email resent'
|
||||||
|
|
||||||
|
def test_it_does_not_call_account_confirmation_email_if_user_is_active(
|
||||||
|
self,
|
||||||
|
app: Flask,
|
||||||
|
user_1: User,
|
||||||
|
account_confirmation_email_mock: Mock,
|
||||||
|
) -> None:
|
||||||
|
client = app.test_client()
|
||||||
|
|
||||||
|
client.post(
|
||||||
|
'/api/auth/account/resend-confirmation',
|
||||||
|
data=json.dumps(dict(email=user_1.email)),
|
||||||
|
content_type='application/json',
|
||||||
|
environ_base={'HTTP_USER_AGENT': USER_AGENT},
|
||||||
|
)
|
||||||
|
|
||||||
|
account_confirmation_email_mock.send.assert_not_called()
|
||||||
|
|
||||||
|
def test_it_returns_success_if_user_is_inactive(
|
||||||
|
self, app: Flask, inactive_user: User
|
||||||
|
) -> None:
|
||||||
|
client = app.test_client()
|
||||||
|
|
||||||
|
response = client.post(
|
||||||
|
'/api/auth/account/resend-confirmation',
|
||||||
|
data=json.dumps(dict(email=inactive_user.email)),
|
||||||
|
content_type='application/json',
|
||||||
|
)
|
||||||
|
|
||||||
|
assert response.status_code == 200
|
||||||
|
data = json.loads(response.data.decode())
|
||||||
|
assert data['status'] == 'success'
|
||||||
|
assert data['message'] == 'confirmation email resent'
|
||||||
|
|
||||||
|
def test_it_updates_token_if_user_is_inactive(
|
||||||
|
self, app: Flask, inactive_user: User
|
||||||
|
) -> None:
|
||||||
|
client = app.test_client()
|
||||||
|
previous_token = inactive_user.confirmation_token
|
||||||
|
|
||||||
|
client.post(
|
||||||
|
'/api/auth/account/resend-confirmation',
|
||||||
|
data=json.dumps(dict(email=inactive_user.email)),
|
||||||
|
content_type='application/json',
|
||||||
|
)
|
||||||
|
|
||||||
|
assert inactive_user.confirmation_token != previous_token
|
||||||
|
|
||||||
|
def test_it_calls_account_confirmation_email_if_user_is_inactive(
|
||||||
|
self,
|
||||||
|
app: Flask,
|
||||||
|
inactive_user: User,
|
||||||
|
account_confirmation_email_mock: Mock,
|
||||||
|
) -> None:
|
||||||
|
client = app.test_client()
|
||||||
|
expected_token = self.random_string()
|
||||||
|
|
||||||
|
with patch('secrets.token_urlsafe', return_value=expected_token):
|
||||||
|
client.post(
|
||||||
|
'/api/auth/account/resend-confirmation',
|
||||||
|
data=json.dumps(dict(email=inactive_user.email)),
|
||||||
|
content_type='application/json',
|
||||||
|
environ_base={'HTTP_USER_AGENT': USER_AGENT},
|
||||||
|
)
|
||||||
|
|
||||||
|
account_confirmation_email_mock.send.assert_called_once_with(
|
||||||
|
{
|
||||||
|
'language': 'en',
|
||||||
|
'email': inactive_user.email,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'username': inactive_user.username,
|
||||||
|
'fittrackee_url': 'http://0.0.0.0:5000',
|
||||||
|
'operating_system': 'linux',
|
||||||
|
'browser_name': 'firefox',
|
||||||
|
'account_confirmation_url': (
|
||||||
|
'http://0.0.0.0:5000/account-confirmation'
|
||||||
|
f'?token={expected_token}'
|
||||||
|
),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
@ -42,6 +42,25 @@ auth_blueprint = Blueprint('auth', __name__)
|
|||||||
HEX_COLOR_REGEX = regex = "^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$"
|
HEX_COLOR_REGEX = regex = "^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$"
|
||||||
|
|
||||||
|
|
||||||
|
def send_account_confirmation_email(user: User) -> None:
|
||||||
|
ui_url = current_app.config['UI_URL']
|
||||||
|
email_data = {
|
||||||
|
'username': user.username,
|
||||||
|
'fittrackee_url': ui_url,
|
||||||
|
'operating_system': request.user_agent.platform, # type: ignore # noqa
|
||||||
|
'browser_name': request.user_agent.browser, # type: ignore
|
||||||
|
'account_confirmation_url': (
|
||||||
|
f'{ui_url}/account-confirmation'
|
||||||
|
f'?token={user.confirmation_token}'
|
||||||
|
),
|
||||||
|
}
|
||||||
|
user_data = {
|
||||||
|
'language': 'en',
|
||||||
|
'email': user.email,
|
||||||
|
}
|
||||||
|
account_confirmation_email.send(user_data, email_data)
|
||||||
|
|
||||||
|
|
||||||
@auth_blueprint.route('/auth/register', methods=['POST'])
|
@auth_blueprint.route('/auth/register', methods=['POST'])
|
||||||
def register_user() -> Union[Tuple[Dict, int], HttpResponse]:
|
def register_user() -> Union[Tuple[Dict, int], HttpResponse]:
|
||||||
"""
|
"""
|
||||||
@ -59,11 +78,11 @@ def register_user() -> Union[Tuple[Dict, int], HttpResponse]:
|
|||||||
|
|
||||||
**Example responses**:
|
**Example responses**:
|
||||||
|
|
||||||
- successful registration
|
- success
|
||||||
|
|
||||||
.. sourcecode:: http
|
.. sourcecode:: http
|
||||||
|
|
||||||
HTTP/1.1 201 CREATED
|
HTTP/1.1 200 SUCCESS
|
||||||
Content-Type: application/json
|
Content-Type: application/json
|
||||||
|
|
||||||
{
|
{
|
||||||
@ -86,7 +105,7 @@ def register_user() -> Union[Tuple[Dict, int], HttpResponse]:
|
|||||||
:<json string email: user email
|
:<json string email: user email
|
||||||
:<json string password: password (8 characters required)
|
:<json string password: password (8 characters required)
|
||||||
|
|
||||||
:statuscode 201: successfully registered
|
:statuscode 200: success
|
||||||
:statuscode 400:
|
:statuscode 400:
|
||||||
- invalid payload
|
- invalid payload
|
||||||
- sorry, that username is already taken
|
- sorry, that username is already taken
|
||||||
@ -105,7 +124,6 @@ def register_user() -> Union[Tuple[Dict, int], HttpResponse]:
|
|||||||
if not current_app.config.get('is_registration_enabled'):
|
if not current_app.config.get('is_registration_enabled'):
|
||||||
return ForbiddenErrorResponse('error, registration is disabled')
|
return ForbiddenErrorResponse('error, registration is disabled')
|
||||||
|
|
||||||
# get post data
|
|
||||||
post_data = request.get_json()
|
post_data = request.get_json()
|
||||||
if (
|
if (
|
||||||
not post_data
|
not post_data
|
||||||
@ -143,26 +161,11 @@ def register_user() -> Union[Tuple[Dict, int], HttpResponse]:
|
|||||||
if not user:
|
if not user:
|
||||||
new_user = User(username=username, email=email, password=password)
|
new_user = User(username=username, email=email, password=password)
|
||||||
new_user.timezone = 'Europe/Paris'
|
new_user.timezone = 'Europe/Paris'
|
||||||
new_user.confirmation_token = secrets.token_urlsafe(16)
|
new_user.confirmation_token = secrets.token_urlsafe(30)
|
||||||
db.session.add(new_user)
|
db.session.add(new_user)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
ui_url = current_app.config['UI_URL']
|
send_account_confirmation_email(new_user)
|
||||||
email_data = {
|
|
||||||
'username': new_user.username,
|
|
||||||
'fittrackee_url': ui_url,
|
|
||||||
'operating_system': request.user_agent.platform, # type: ignore # noqa
|
|
||||||
'browser_name': request.user_agent.browser, # type: ignore
|
|
||||||
'account_confirmation_url': (
|
|
||||||
f'{ui_url}/account-confirmation'
|
|
||||||
f'?token={new_user.confirmation_token}'
|
|
||||||
),
|
|
||||||
}
|
|
||||||
user_data = {
|
|
||||||
'language': 'en',
|
|
||||||
'email': new_user.email,
|
|
||||||
}
|
|
||||||
account_confirmation_email.send(user_data, email_data)
|
|
||||||
|
|
||||||
return {'status': 'success'}, 200
|
return {'status': 'success'}, 200
|
||||||
# handler errors
|
# handler errors
|
||||||
@ -634,7 +637,7 @@ def update_user_account(auth_user: User) -> Union[Dict, HttpResponse]:
|
|||||||
if email_to_confirm != auth_user.email:
|
if email_to_confirm != auth_user.email:
|
||||||
if is_valid_email(email_to_confirm):
|
if is_valid_email(email_to_confirm):
|
||||||
auth_user.email_to_confirm = email_to_confirm
|
auth_user.email_to_confirm = email_to_confirm
|
||||||
auth_user.confirmation_token = secrets.token_urlsafe(16)
|
auth_user.confirmation_token = secrets.token_urlsafe(30)
|
||||||
else:
|
else:
|
||||||
error_messages = 'email: valid email must be provided\n'
|
error_messages = 'email: valid email must be provided\n'
|
||||||
|
|
||||||
@ -1393,3 +1396,54 @@ def confirm_account() -> Union[Dict, HttpResponse]:
|
|||||||
|
|
||||||
except (exc.OperationalError, ValueError) as e:
|
except (exc.OperationalError, ValueError) as e:
|
||||||
return handle_error_and_return_response(e, db=db)
|
return handle_error_and_return_response(e, db=db)
|
||||||
|
|
||||||
|
|
||||||
|
@auth_blueprint.route('/auth/account/resend-confirmation', methods=['POST'])
|
||||||
|
def resend_account_confirmation_email() -> Union[Dict, HttpResponse]:
|
||||||
|
"""
|
||||||
|
resend email with instructions to confirm account
|
||||||
|
|
||||||
|
**Example request**:
|
||||||
|
|
||||||
|
.. sourcecode:: http
|
||||||
|
|
||||||
|
POST /api/auth/account/resend-confirmation HTTP/1.1
|
||||||
|
Content-Type: application/json
|
||||||
|
|
||||||
|
**Example response**:
|
||||||
|
|
||||||
|
HTTP/1.1 200 OK
|
||||||
|
Content-Type: application/json
|
||||||
|
|
||||||
|
{
|
||||||
|
"message": "confirmation email resent",
|
||||||
|
"status": "success"
|
||||||
|
}
|
||||||
|
|
||||||
|
:<json string email: user email
|
||||||
|
|
||||||
|
:statuscode 200: confirmation email resent
|
||||||
|
:statuscode 400: invalid payload
|
||||||
|
:statuscode 500: error, please try again or contact the administrator
|
||||||
|
|
||||||
|
"""
|
||||||
|
post_data = request.get_json()
|
||||||
|
if not post_data or post_data.get('email') is None:
|
||||||
|
return InvalidPayloadErrorResponse()
|
||||||
|
email = post_data.get('email')
|
||||||
|
|
||||||
|
try:
|
||||||
|
user = User.query.filter_by(email=email, is_active=False).first()
|
||||||
|
if user:
|
||||||
|
user.confirmation_token = secrets.token_urlsafe(30)
|
||||||
|
db.session.commit()
|
||||||
|
|
||||||
|
send_account_confirmation_email(user)
|
||||||
|
|
||||||
|
response = {
|
||||||
|
'status': 'success',
|
||||||
|
'message': 'confirmation email resent',
|
||||||
|
}
|
||||||
|
return response
|
||||||
|
except (exc.OperationalError, ValueError) as e:
|
||||||
|
return handle_error_and_return_response(e, db=db)
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import os
|
import os
|
||||||
import random
|
|
||||||
import secrets
|
import secrets
|
||||||
import shutil
|
import shutil
|
||||||
from typing import Any, Dict, Tuple, Union
|
from typing import Any, Dict, Tuple, Union
|
||||||
@ -517,7 +516,7 @@ def update_user(auth_user: User, user_name: str) -> Union[Dict, HttpResponse]:
|
|||||||
'reset_password' in user_data
|
'reset_password' in user_data
|
||||||
and user_data['reset_password'] is True
|
and user_data['reset_password'] is True
|
||||||
):
|
):
|
||||||
new_password = secrets.token_urlsafe(random.randint(16, 20))
|
new_password = secrets.token_urlsafe(30)
|
||||||
user.password = bcrypt.generate_password_hash(
|
user.password = bcrypt.generate_password_hash(
|
||||||
new_password, current_app.config.get('BCRYPT_LOG_ROUNDS')
|
new_password, current_app.config.get('BCRYPT_LOG_ROUNDS')
|
||||||
).decode()
|
).decode()
|
||||||
@ -526,7 +525,7 @@ def update_user(auth_user: User, user_name: str) -> Union[Dict, HttpResponse]:
|
|||||||
if 'new_email' in user_data:
|
if 'new_email' in user_data:
|
||||||
if is_valid_email(user_data['new_email']):
|
if is_valid_email(user_data['new_email']):
|
||||||
user.email_to_confirm = user_data['new_email']
|
user.email_to_confirm = user_data['new_email']
|
||||||
user.confirmation_token = secrets.token_urlsafe(16)
|
user.confirmation_token = secrets.token_urlsafe(30)
|
||||||
send_new_address_email = True
|
send_new_address_email = True
|
||||||
else:
|
else:
|
||||||
return InvalidPayloadErrorResponse(
|
return InvalidPayloadErrorResponse(
|
||||||
|
@ -0,0 +1,72 @@
|
|||||||
|
<template>
|
||||||
|
<div id="account-confirmation-email" class="center-card with-margin">
|
||||||
|
<div class="email-sent" v-if="action === 'email-sent'">
|
||||||
|
<EmailSent />
|
||||||
|
<div class="email-sent-message">
|
||||||
|
{{ $t('user.ACCOUNT_CONFIRMATION_SENT') }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-else>
|
||||||
|
<Card>
|
||||||
|
<template #title>{{ $t('user.RESENT_ACCOUNT_CONFIRMATION') }}</template>
|
||||||
|
<template #content>
|
||||||
|
<UserAuthForm :action="action" />
|
||||||
|
</template>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { toRefs } from 'vue'
|
||||||
|
|
||||||
|
import EmailSent from '@/components/Common/Images/EmailSent.vue'
|
||||||
|
import UserAuthForm from '@/components/User/UserAuthForm.vue'
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
action: string
|
||||||
|
}
|
||||||
|
const props = defineProps<Props>()
|
||||||
|
|
||||||
|
const { action } = toRefs(props)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
@import '~@/scss/vars.scss';
|
||||||
|
|
||||||
|
#account-confirmation-email {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
.email-sent {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
svg {
|
||||||
|
stroke: none;
|
||||||
|
fill-rule: nonzero;
|
||||||
|
fill: var(--app-color);
|
||||||
|
filter: var(--svg-filter);
|
||||||
|
width: 100px;
|
||||||
|
}
|
||||||
|
.email-sent-message {
|
||||||
|
font-size: 1.1em;
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
@media screen and (max-width: $medium-limit) {
|
||||||
|
font-size: 1em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
::v-deep(.card) {
|
||||||
|
.card-content {
|
||||||
|
#user-auth-form {
|
||||||
|
margin-top: 0;
|
||||||
|
#user-form {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
@ -60,14 +60,24 @@
|
|||||||
:placeholder="$t('user.EMAIL')"
|
:placeholder="$t('user.EMAIL')"
|
||||||
/>
|
/>
|
||||||
<div
|
<div
|
||||||
v-if="['reset-request', 'register'].includes(action)"
|
v-if="
|
||||||
|
[
|
||||||
|
'reset-request',
|
||||||
|
'register',
|
||||||
|
'account-confirmation-resend',
|
||||||
|
].includes(action)
|
||||||
|
"
|
||||||
class="form-info"
|
class="form-info"
|
||||||
>
|
>
|
||||||
<i class="fa fa-info-circle" aria-hidden="true" />
|
<i class="fa fa-info-circle" aria-hidden="true" />
|
||||||
{{ $t('user.EMAIL_INFO') }}
|
{{ $t('user.EMAIL_INFO') }}
|
||||||
</div>
|
</div>
|
||||||
<PasswordInput
|
<PasswordInput
|
||||||
v-if="action !== 'reset-request'"
|
v-if="
|
||||||
|
!['account-confirmation-resend', 'reset-request'].includes(
|
||||||
|
action
|
||||||
|
)
|
||||||
|
"
|
||||||
:disabled="registration_disabled"
|
:disabled="registration_disabled"
|
||||||
:required="true"
|
:required="true"
|
||||||
:placeholder="
|
:placeholder="
|
||||||
@ -100,6 +110,11 @@
|
|||||||
{{ $t('user.LOGIN') }}
|
{{ $t('user.LOGIN') }}
|
||||||
</router-link>
|
</router-link>
|
||||||
</div>
|
</div>
|
||||||
|
<div v-if="['login', 'register'].includes(action)">
|
||||||
|
<router-link class="links" to="/account-confirmation/resend">
|
||||||
|
{{ $t('user.ACCOUNT_CONFIRMATION_NOT_RECEIVED') }}
|
||||||
|
</router-link>
|
||||||
|
</div>
|
||||||
<ErrorMessage :message="errorMessages" v-if="errorMessages" />
|
<ErrorMessage :message="errorMessages" v-if="errorMessages" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -197,6 +212,13 @@
|
|||||||
email: formData.email,
|
email: formData.email,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
case 'account-confirmation-resend':
|
||||||
|
return store.dispatch(
|
||||||
|
AUTH_USER_STORE.ACTIONS.RESEND_ACCOUNT_CONFIRMATION_EMAIL,
|
||||||
|
{
|
||||||
|
email: formData.email,
|
||||||
|
}
|
||||||
|
)
|
||||||
default:
|
default:
|
||||||
store.dispatch(AUTH_USER_STORE.ACTIONS.LOGIN_OR_REGISTER, {
|
store.dispatch(AUTH_USER_STORE.ACTIONS.LOGIN_OR_REGISTER, {
|
||||||
actionType,
|
actionType,
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
{
|
{
|
||||||
|
"ACCOUNT-CONFIRMATION-RESEND": "Resend confirmation email",
|
||||||
"BACK": "Back",
|
"BACK": "Back",
|
||||||
"CANCEL": "Cancel",
|
"CANCEL": "Cancel",
|
||||||
"CLEAR_FILTER": "Clear filters",
|
"CLEAR_FILTER": "Clear filters",
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
{
|
{
|
||||||
|
"ACCOUNT_CONFIRMATION_NOT_RECEIVED": "Didn't received instructions?",
|
||||||
|
"ACCOUNT_CONFIRMATION_SENT": "Check your email. A new confirmation email has been sent to the address provided.",
|
||||||
"ADMIN": "Admin",
|
"ADMIN": "Admin",
|
||||||
"ALREADY_HAVE_ACCOUNT": "Already have an account?",
|
"ALREADY_HAVE_ACCOUNT": "Already have an account?",
|
||||||
"CONFIRM_ACCOUNT_DELETION": "Are you sure you want to delete your account? All data will be deleted, this cannot be undone",
|
"CONFIRM_ACCOUNT_DELETION": "Are you sure you want to delete your account? All data will be deleted, this cannot be undone",
|
||||||
@ -94,6 +96,7 @@
|
|||||||
"TIMEZONE": "Timezone"
|
"TIMEZONE": "Timezone"
|
||||||
},
|
},
|
||||||
"REGISTER": "Register",
|
"REGISTER": "Register",
|
||||||
|
"RESENT_ACCOUNT_CONFIRMATION": "Resend account confirmation email",
|
||||||
"REGISTER_DISABLED": "Sorry, registration is disabled.",
|
"REGISTER_DISABLED": "Sorry, registration is disabled.",
|
||||||
"RESET_PASSWORD": "Reset your password",
|
"RESET_PASSWORD": "Reset your password",
|
||||||
"SHOW_PASSWORD": "show password",
|
"SHOW_PASSWORD": "show password",
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
{
|
{
|
||||||
|
"ACCOUNT-CONFIRMATION-RESEND": "Envoyer à nouveau l'email de confirmation",
|
||||||
"BACK": "Précédent",
|
"BACK": "Précédent",
|
||||||
"CANCEL": "Annuler",
|
"CANCEL": "Annuler",
|
||||||
"CLEAR_FILTER": "Réinitialiser",
|
"CLEAR_FILTER": "Réinitialiser",
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
{
|
{
|
||||||
|
"ACCOUNT_CONFIRMATION_NOT_RECEIVED": "Vous n'avez pas reçu les instructions ?",
|
||||||
|
"ACCOUNT_CONFIRMATION_SENT": "Vérifiez votre boite mail. Un nouvel email de confirmation a été envoyé à l'adresse email fournie.",
|
||||||
"ADMIN": "Admin",
|
"ADMIN": "Admin",
|
||||||
"ALREADY_HAVE_ACCOUNT": "Vous avez déjà un compte ?",
|
"ALREADY_HAVE_ACCOUNT": "Vous avez déjà un compte ?",
|
||||||
"CONFIRM_ACCOUNT_DELETION": "Êtes-vous sûr de vouloir supprimer votre compte ? Toutes les données seront définitivement effacés.",
|
"CONFIRM_ACCOUNT_DELETION": "Êtes-vous sûr de vouloir supprimer votre compte ? Toutes les données seront définitivement effacés.",
|
||||||
@ -94,6 +96,7 @@
|
|||||||
},
|
},
|
||||||
"REGISTER": "S'inscrire",
|
"REGISTER": "S'inscrire",
|
||||||
"REGISTER_DISABLED": "Désolé, les inscriptions sont désactivées.",
|
"REGISTER_DISABLED": "Désolé, les inscriptions sont désactivées.",
|
||||||
|
"RESENT_ACCOUNT_CONFIRMATION": "Envoyer à nouveau l'email de confirmation de compte",
|
||||||
"RESET_PASSWORD": "Réinitialiser votre mot de passe",
|
"RESET_PASSWORD": "Réinitialiser votre mot de passe",
|
||||||
"SHOW_PASSWORD": "afficher le mot de passe",
|
"SHOW_PASSWORD": "afficher le mot de passe",
|
||||||
"USER_PICTURE": "photo de l'utilisateur",
|
"USER_PICTURE": "photo de l'utilisateur",
|
||||||
|
@ -43,6 +43,32 @@ const routes: Array<RouteRecordRaw> = [
|
|||||||
component: LoginOrRegister,
|
component: LoginOrRegister,
|
||||||
props: { action: 'register' },
|
props: { action: 'register' },
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: '/account-confirmation',
|
||||||
|
name: 'AccountConfirmation',
|
||||||
|
component: () =>
|
||||||
|
import(
|
||||||
|
/* webpackChunkName: 'profile' */ '@/views/user/AccountConfirmation.vue'
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/account-confirmation/resend',
|
||||||
|
name: 'AccountConfirmationResend',
|
||||||
|
component: () =>
|
||||||
|
import(
|
||||||
|
/* webpackChunkName: 'reset' */ '@/views/user/AccountConfirmationResendView.vue'
|
||||||
|
),
|
||||||
|
props: { action: 'account-confirmation-resend' },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/account-confirmation/email-sent',
|
||||||
|
name: 'AccountConfirmationEmailSend',
|
||||||
|
component: () =>
|
||||||
|
import(
|
||||||
|
/* webpackChunkName: 'reset' */ '@/views/user/AccountConfirmationResendView.vue'
|
||||||
|
),
|
||||||
|
props: { action: 'email-sent' },
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: '/password-reset/sent',
|
path: '/password-reset/sent',
|
||||||
name: 'PasswordEmailSent',
|
name: 'PasswordEmailSent',
|
||||||
@ -79,14 +105,6 @@ const routes: Array<RouteRecordRaw> = [
|
|||||||
),
|
),
|
||||||
props: { action: 'reset' },
|
props: { action: 'reset' },
|
||||||
},
|
},
|
||||||
{
|
|
||||||
path: '/account-confirmation',
|
|
||||||
name: 'AccountConfirmation',
|
|
||||||
component: () =>
|
|
||||||
import(
|
|
||||||
/* webpackChunkName: 'profile' */ '@/views/user/AccountConfirmation.vue'
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
path: '/email-update',
|
path: '/email-update',
|
||||||
name: 'EmailUpdate',
|
name: 'EmailUpdate',
|
||||||
@ -276,6 +294,8 @@ const pathsWithoutAuthentication = [
|
|||||||
'/password-reset/sent',
|
'/password-reset/sent',
|
||||||
'/register',
|
'/register',
|
||||||
'/account-confirmation',
|
'/account-confirmation',
|
||||||
|
'/account-confirmation/resend',
|
||||||
|
'/account-confirmation/email-sent',
|
||||||
]
|
]
|
||||||
|
|
||||||
const pathsWithoutChecks = ['/email-update']
|
const pathsWithoutChecks = ['/email-update']
|
||||||
|
@ -23,7 +23,7 @@ import {
|
|||||||
IUserAccountPayload,
|
IUserAccountPayload,
|
||||||
IUserDeletionPayload,
|
IUserDeletionPayload,
|
||||||
IUserAccountUpdatePayload,
|
IUserAccountUpdatePayload,
|
||||||
IUserPasswordPayload,
|
IUserEmailPayload,
|
||||||
IUserPasswordResetPayload,
|
IUserPasswordResetPayload,
|
||||||
IUserPayload,
|
IUserPayload,
|
||||||
IUserPicturePayload,
|
IUserPicturePayload,
|
||||||
@ -366,7 +366,7 @@ export const actions: ActionTree<IAuthUserState, IRootState> &
|
|||||||
},
|
},
|
||||||
[AUTH_USER_STORE.ACTIONS.SEND_PASSWORD_RESET_REQUEST](
|
[AUTH_USER_STORE.ACTIONS.SEND_PASSWORD_RESET_REQUEST](
|
||||||
context: ActionContext<IAuthUserState, IRootState>,
|
context: ActionContext<IAuthUserState, IRootState>,
|
||||||
payload: IUserPasswordPayload
|
payload: IUserEmailPayload
|
||||||
): void {
|
): void {
|
||||||
context.commit(ROOT_STORE.MUTATIONS.EMPTY_ERROR_MESSAGES)
|
context.commit(ROOT_STORE.MUTATIONS.EMPTY_ERROR_MESSAGES)
|
||||||
api
|
api
|
||||||
@ -380,6 +380,22 @@ export const actions: ActionTree<IAuthUserState, IRootState> &
|
|||||||
})
|
})
|
||||||
.catch((error) => handleError(context, error))
|
.catch((error) => handleError(context, error))
|
||||||
},
|
},
|
||||||
|
[AUTH_USER_STORE.ACTIONS.RESEND_ACCOUNT_CONFIRMATION_EMAIL](
|
||||||
|
context: ActionContext<IAuthUserState, IRootState>,
|
||||||
|
payload: IUserEmailPayload
|
||||||
|
): void {
|
||||||
|
context.commit(ROOT_STORE.MUTATIONS.EMPTY_ERROR_MESSAGES)
|
||||||
|
api
|
||||||
|
.post('auth/account/resend-confirmation', payload)
|
||||||
|
.then((res) => {
|
||||||
|
if (res.data.status === 'success') {
|
||||||
|
router.push('/account-confirmation/email-sent')
|
||||||
|
} else {
|
||||||
|
handleError(context, null)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((error) => handleError(context, error))
|
||||||
|
},
|
||||||
[AUTH_USER_STORE.ACTIONS.RESET_USER_PASSWORD](
|
[AUTH_USER_STORE.ACTIONS.RESET_USER_PASSWORD](
|
||||||
context: ActionContext<IAuthUserState, IRootState>,
|
context: ActionContext<IAuthUserState, IRootState>,
|
||||||
payload: IUserPasswordResetPayload
|
payload: IUserPasswordResetPayload
|
||||||
|
@ -8,6 +8,7 @@ export enum AuthUserActions {
|
|||||||
LOGIN_OR_REGISTER = 'LOGIN_OR_REGISTER',
|
LOGIN_OR_REGISTER = 'LOGIN_OR_REGISTER',
|
||||||
LOGOUT = 'LOGOUT',
|
LOGOUT = 'LOGOUT',
|
||||||
SEND_PASSWORD_RESET_REQUEST = 'SEND_PASSWORD_RESET_REQUEST',
|
SEND_PASSWORD_RESET_REQUEST = 'SEND_PASSWORD_RESET_REQUEST',
|
||||||
|
RESEND_ACCOUNT_CONFIRMATION_EMAIL = 'RESEND_ACCOUNT_CONFIRMATION_EMAIL',
|
||||||
RESET_USER_PASSWORD = 'RESET_USER_PASSWORD',
|
RESET_USER_PASSWORD = 'RESET_USER_PASSWORD',
|
||||||
RESET_USER_SPORT_PREFERENCES = 'RESET_USER_SPORT_PREFERENCES',
|
RESET_USER_SPORT_PREFERENCES = 'RESET_USER_SPORT_PREFERENCES',
|
||||||
UPDATE_USER_ACCOUNT = 'UPDATE_USER_ACCOUNT',
|
UPDATE_USER_ACCOUNT = 'UPDATE_USER_ACCOUNT',
|
||||||
|
@ -11,7 +11,7 @@ import {
|
|||||||
IAuthUserProfile,
|
IAuthUserProfile,
|
||||||
ILoginOrRegisterData,
|
ILoginOrRegisterData,
|
||||||
IUserDeletionPayload,
|
IUserDeletionPayload,
|
||||||
IUserPasswordPayload,
|
IUserEmailPayload,
|
||||||
IUserPasswordResetPayload,
|
IUserPasswordResetPayload,
|
||||||
IUserPayload,
|
IUserPayload,
|
||||||
IUserPicturePayload,
|
IUserPicturePayload,
|
||||||
@ -84,7 +84,12 @@ export interface IAuthUserActions {
|
|||||||
|
|
||||||
[AUTH_USER_STORE.ACTIONS.SEND_PASSWORD_RESET_REQUEST](
|
[AUTH_USER_STORE.ACTIONS.SEND_PASSWORD_RESET_REQUEST](
|
||||||
context: ActionContext<IAuthUserState, IRootState>,
|
context: ActionContext<IAuthUserState, IRootState>,
|
||||||
payload: IUserPasswordPayload
|
payload: IUserEmailPayload
|
||||||
|
): void
|
||||||
|
|
||||||
|
[AUTH_USER_STORE.ACTIONS.RESEND_ACCOUNT_CONFIRMATION_EMAIL](
|
||||||
|
context: ActionContext<IAuthUserState, IRootState>,
|
||||||
|
payload: IUserEmailPayload
|
||||||
): void
|
): void
|
||||||
|
|
||||||
[AUTH_USER_STORE.ACTIONS.RESET_USER_PASSWORD](
|
[AUTH_USER_STORE.ACTIONS.RESET_USER_PASSWORD](
|
||||||
|
@ -72,7 +72,7 @@ export interface IUserPicturePayload {
|
|||||||
picture: File
|
picture: File
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IUserPasswordPayload {
|
export interface IUserEmailPayload {
|
||||||
email: string
|
email: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,34 @@
|
|||||||
|
<template>
|
||||||
|
<div id="account-confirmation" class="view">
|
||||||
|
<div class="container">
|
||||||
|
<AccountConfirmationResend :action="action" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { toRefs } from 'vue'
|
||||||
|
|
||||||
|
import AccountConfirmationResend from '@/components/User/AccountConfirmationEmail.vue'
|
||||||
|
interface Props {
|
||||||
|
action: string
|
||||||
|
}
|
||||||
|
const props = defineProps<Props>()
|
||||||
|
const { action } = toRefs(props)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
@import '~@/scss/vars.scss';
|
||||||
|
|
||||||
|
#account-confirmation {
|
||||||
|
display: flex;
|
||||||
|
.container {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
width: 50%;
|
||||||
|
|
||||||
|
@media screen and (max-width: $small-limit) {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
Loading…
Reference in New Issue
Block a user