API - use task queue to send password request email
This commit is contained in:
parent
e990a63675
commit
c797d4393e
3
Makefile
3
Makefile
@ -87,6 +87,9 @@ run-client:
|
|||||||
run-server:
|
run-server:
|
||||||
cd fittrackee_api && $(GUNICORN) -b 127.0.0.1:5000 "fittrackee_api:create_app()" --error-logfile ../gunicorn-error.log
|
cd fittrackee_api && $(GUNICORN) -b 127.0.0.1:5000 "fittrackee_api:create_app()" --error-logfile ../gunicorn-error.log
|
||||||
|
|
||||||
|
run-workers:
|
||||||
|
$(FLASK) worker --processes=1
|
||||||
|
|
||||||
serve-python:
|
serve-python:
|
||||||
$(FLASK) run --with-threads -h $(HOST) -p $(API_PORT)
|
$(FLASK) run --with-threads -h $(HOST) -p $(API_PORT)
|
||||||
|
|
||||||
|
@ -4,6 +4,7 @@ from importlib import import_module, reload
|
|||||||
|
|
||||||
from flask import Flask
|
from flask import Flask
|
||||||
from flask_bcrypt import Bcrypt
|
from flask_bcrypt import Bcrypt
|
||||||
|
from flask_dramatiq import Dramatiq
|
||||||
from flask_migrate import Migrate
|
from flask_migrate import Migrate
|
||||||
from flask_sqlalchemy import SQLAlchemy
|
from flask_sqlalchemy import SQLAlchemy
|
||||||
|
|
||||||
@ -13,6 +14,7 @@ db = SQLAlchemy()
|
|||||||
bcrypt = Bcrypt()
|
bcrypt = Bcrypt()
|
||||||
migrate = Migrate()
|
migrate = Migrate()
|
||||||
email_service = Email()
|
email_service = Email()
|
||||||
|
dramatiq = Dramatiq()
|
||||||
appLog = logging.getLogger('fittrackee_api')
|
appLog = logging.getLogger('fittrackee_api')
|
||||||
|
|
||||||
|
|
||||||
@ -33,6 +35,7 @@ def create_app():
|
|||||||
db.init_app(app)
|
db.init_app(app)
|
||||||
bcrypt.init_app(app)
|
bcrypt.init_app(app)
|
||||||
migrate.init_app(app, db)
|
migrate.init_app(app, db)
|
||||||
|
dramatiq.init_app(app)
|
||||||
|
|
||||||
# set up email
|
# set up email
|
||||||
email_service.init_email(app)
|
email_service.init_email(app)
|
||||||
|
@ -1,7 +1,15 @@
|
|||||||
import os
|
import os
|
||||||
|
|
||||||
|
from dramatiq.brokers.redis import RedisBroker
|
||||||
|
from dramatiq.brokers.stub import StubBroker
|
||||||
from flask import current_app
|
from flask import current_app
|
||||||
|
|
||||||
|
if os.getenv('APP_SETTINGS') == 'fittrackee_api.config.Testing':
|
||||||
|
broker = StubBroker
|
||||||
|
broker.emit_after("process_boot")
|
||||||
|
else:
|
||||||
|
broker = RedisBroker
|
||||||
|
|
||||||
|
|
||||||
class BaseConfig:
|
class BaseConfig:
|
||||||
"""Base configuration"""
|
"""Base configuration"""
|
||||||
@ -20,6 +28,7 @@ class BaseConfig:
|
|||||||
UI_URL = os.environ.get('UI_URL')
|
UI_URL = os.environ.get('UI_URL')
|
||||||
EMAIL_URL = os.environ.get('EMAIL_URL')
|
EMAIL_URL = os.environ.get('EMAIL_URL')
|
||||||
SENDER_EMAIL = os.environ.get('SENDER_EMAIL')
|
SENDER_EMAIL = os.environ.get('SENDER_EMAIL')
|
||||||
|
DRAMATIQ_BROKER = broker
|
||||||
|
|
||||||
|
|
||||||
class DevelopmentConfig(BaseConfig):
|
class DevelopmentConfig(BaseConfig):
|
||||||
@ -31,6 +40,7 @@ class DevelopmentConfig(BaseConfig):
|
|||||||
USERNAME = 'admin'
|
USERNAME = 'admin'
|
||||||
PASSWORD = 'default'
|
PASSWORD = 'default'
|
||||||
BCRYPT_LOG_ROUNDS = 4
|
BCRYPT_LOG_ROUNDS = 4
|
||||||
|
DRAMATIQ_BROKER_URL = os.getenv('REDIS_URL', 'redis://')
|
||||||
|
|
||||||
|
|
||||||
class TestingConfig(BaseConfig):
|
class TestingConfig(BaseConfig):
|
||||||
|
11
fittrackee_api/fittrackee_api/tasks.py
Normal file
11
fittrackee_api/fittrackee_api/tasks.py
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
from fittrackee_api import dramatiq, email_service
|
||||||
|
|
||||||
|
|
||||||
|
@dramatiq.actor()
|
||||||
|
def reset_password_email(user, email_data):
|
||||||
|
email_service.send(
|
||||||
|
template='password_reset_request',
|
||||||
|
lang=user['language'],
|
||||||
|
recipient=user['email'],
|
||||||
|
data=email_data,
|
||||||
|
)
|
@ -8,10 +8,10 @@ from fittrackee_api.application.models import AppConfig
|
|||||||
from fittrackee_api.application.utils import update_app_config_from_database
|
from fittrackee_api.application.utils import update_app_config_from_database
|
||||||
from fittrackee_api.users.models import User
|
from fittrackee_api.users.models import User
|
||||||
|
|
||||||
os.environ["FLASK_ENV"] = 'testing'
|
os.environ['FLASK_ENV'] = 'testing'
|
||||||
os.environ["APP_SETTINGS"] = 'fittrackee_api.config.TestingConfig'
|
os.environ['APP_SETTINGS'] = 'fittrackee_api.config.TestingConfig'
|
||||||
# to avoid resetting dev database during tests
|
# to avoid resetting dev database during tests
|
||||||
os.environ["DATABASE_URL"] = os.getenv("DATABASE_TEST_URL")
|
os.environ['DATABASE_URL'] = os.getenv('DATABASE_TEST_URL')
|
||||||
|
|
||||||
|
|
||||||
def get_app_config(with_config=False):
|
def get_app_config(with_config=False):
|
||||||
|
@ -2,7 +2,8 @@ import datetime
|
|||||||
import os
|
import os
|
||||||
|
|
||||||
import jwt
|
import jwt
|
||||||
from fittrackee_api import appLog, bcrypt, db, email_service
|
from fittrackee_api import appLog, bcrypt, db
|
||||||
|
from fittrackee_api.tasks import reset_password_email
|
||||||
from flask import Blueprint, current_app, jsonify, request
|
from flask import Blueprint, current_app, jsonify, request
|
||||||
from sqlalchemy import exc, or_
|
from sqlalchemy import exc, or_
|
||||||
from werkzeug.exceptions import RequestEntityTooLarge
|
from werkzeug.exceptions import RequestEntityTooLarge
|
||||||
@ -712,12 +713,11 @@ def request_password_reset():
|
|||||||
'operating_system': request.user_agent.platform,
|
'operating_system': request.user_agent.platform,
|
||||||
'browser_name': request.user_agent.browser,
|
'browser_name': request.user_agent.browser,
|
||||||
}
|
}
|
||||||
email_service.send(
|
user_data = {
|
||||||
template='password_reset_request',
|
'language': user.language if user.language else 'en',
|
||||||
lang=user.language if user.language else 'en',
|
'email': user.email,
|
||||||
recipient=user.email,
|
}
|
||||||
data=email_data,
|
reset_password_email.send(user_data, email_data)
|
||||||
)
|
|
||||||
response_object = {
|
response_object = {
|
||||||
'status': 'success',
|
'status': 'success',
|
||||||
'message': 'Password reset request processed.',
|
'message': 'Password reset request processed.',
|
||||||
|
74
fittrackee_api/poetry.lock
generated
74
fittrackee_api/poetry.lock
generated
@ -205,6 +205,29 @@ optional = false
|
|||||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
|
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
|
||||||
version = "0.16"
|
version = "0.16"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
category = "main"
|
||||||
|
description = "Background Processing for Python 3."
|
||||||
|
name = "dramatiq"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.5"
|
||||||
|
version = "1.9.0"
|
||||||
|
|
||||||
|
[package.dependencies]
|
||||||
|
prometheus-client = ">=0.2"
|
||||||
|
|
||||||
|
[package.dependencies.redis]
|
||||||
|
optional = true
|
||||||
|
version = ">=2.0,<4.0"
|
||||||
|
|
||||||
|
[package.extras]
|
||||||
|
all = ["watchdog-gevent (0.1)", "watchdog (>=0.8,<0.9)", "pylibmc (>=1.5,<2.0)", "pika (>=1.0,<2.0)", "redis (>=2.0,<4.0)"]
|
||||||
|
dev = ["watchdog-gevent (0.1)", "watchdog (>=0.8,<0.9)", "pylibmc (>=1.5,<2.0)", "pika (>=1.0,<2.0)", "redis (>=2.0,<4.0)", "alabaster", "sphinx (<1.8)", "sphinxcontrib-napoleon", "flake8", "flake8-bugbear", "flake8-quotes", "isort", "bumpversion", "hiredis", "twine", "wheel", "pytest (<4)", "pytest-benchmark", "pytest-cov", "tox"]
|
||||||
|
memcached = ["pylibmc (>=1.5,<2.0)"]
|
||||||
|
rabbitmq = ["pika (>=1.0,<2.0)"]
|
||||||
|
redis = ["redis (>=2.0,<4.0)"]
|
||||||
|
watch = ["watchdog (>=0.8,<0.9)", "watchdog-gevent (0.1)"]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
category = "dev"
|
category = "dev"
|
||||||
description = "the modular source code checker: pep8 pyflakes and co"
|
description = "the modular source code checker: pep8 pyflakes and co"
|
||||||
@ -253,6 +276,17 @@ version = "0.7.1"
|
|||||||
Flask = "*"
|
Flask = "*"
|
||||||
bcrypt = "*"
|
bcrypt = "*"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
category = "main"
|
||||||
|
description = "Adds Dramatiq support to your Flask application"
|
||||||
|
name = "flask-dramatiq"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.6,<4.0"
|
||||||
|
version = "0.6.0"
|
||||||
|
|
||||||
|
[package.dependencies]
|
||||||
|
dramatiq = ">=1.5,<2.0"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
category = "main"
|
category = "main"
|
||||||
description = "SQLAlchemy database migrations for Flask applications using Alembic"
|
description = "SQLAlchemy database migrations for Flask applications using Alembic"
|
||||||
@ -475,6 +509,17 @@ version = ">=0.12"
|
|||||||
[package.extras]
|
[package.extras]
|
||||||
dev = ["pre-commit", "tox"]
|
dev = ["pre-commit", "tox"]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
category = "main"
|
||||||
|
description = "Python client for the Prometheus monitoring system."
|
||||||
|
name = "prometheus-client"
|
||||||
|
optional = false
|
||||||
|
python-versions = "*"
|
||||||
|
version = "0.8.0"
|
||||||
|
|
||||||
|
[package.extras]
|
||||||
|
twisted = ["twisted"]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
category = "main"
|
category = "main"
|
||||||
description = "psycopg2 - Python-PostgreSQL Database Adapter"
|
description = "psycopg2 - Python-PostgreSQL Database Adapter"
|
||||||
@ -708,6 +753,17 @@ commonmark = ">=0.8.1"
|
|||||||
docutils = ">=0.11"
|
docutils = ">=0.11"
|
||||||
sphinx = ">=1.3.1"
|
sphinx = ">=1.3.1"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
category = "main"
|
||||||
|
description = "Python client for Redis key-value store"
|
||||||
|
name = "redis"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
|
||||||
|
version = "3.5.3"
|
||||||
|
|
||||||
|
[package.extras]
|
||||||
|
hiredis = ["hiredis (>=0.1.3)"]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
category = "dev"
|
category = "dev"
|
||||||
description = "Alternative regular expression module, to replace re."
|
description = "Alternative regular expression module, to replace re."
|
||||||
@ -997,7 +1053,7 @@ docs = ["sphinx", "jaraco.packaging (>=3.2)", "rst.linker (>=1.9)"]
|
|||||||
testing = ["jaraco.itertools", "func-timeout"]
|
testing = ["jaraco.itertools", "func-timeout"]
|
||||||
|
|
||||||
[metadata]
|
[metadata]
|
||||||
content-hash = "a9201e432af36210daf0112aafdd4bc39ba5a7299f4010bf4919c85340ab40b9"
|
content-hash = "b8d219c99cac540afe974e9170fad8b4b0e1cd726148389edfcf29e2233cc121"
|
||||||
python-versions = "^3.7"
|
python-versions = "^3.7"
|
||||||
|
|
||||||
[metadata.files]
|
[metadata.files]
|
||||||
@ -1163,6 +1219,10 @@ docutils = [
|
|||||||
{file = "docutils-0.16-py2.py3-none-any.whl", hash = "sha256:0c5b78adfbf7762415433f5515cd5c9e762339e23369dbe8000d84a4bf4ab3af"},
|
{file = "docutils-0.16-py2.py3-none-any.whl", hash = "sha256:0c5b78adfbf7762415433f5515cd5c9e762339e23369dbe8000d84a4bf4ab3af"},
|
||||||
{file = "docutils-0.16.tar.gz", hash = "sha256:c2de3a60e9e7d07be26b7f2b00ca0309c207e06c100f9cc2a94931fc75a478fc"},
|
{file = "docutils-0.16.tar.gz", hash = "sha256:c2de3a60e9e7d07be26b7f2b00ca0309c207e06c100f9cc2a94931fc75a478fc"},
|
||||||
]
|
]
|
||||||
|
dramatiq = [
|
||||||
|
{file = "dramatiq-1.9.0-py3-none-any.whl", hash = "sha256:360cd436a434a513c87a9769943543c1d065835e3fa0b01f96c4fdd959bfa1c3"},
|
||||||
|
{file = "dramatiq-1.9.0.tar.gz", hash = "sha256:8112941ab2eda4f0288bacd137a991f9b1b1c600fe3dd5960eaba4256c873839"},
|
||||||
|
]
|
||||||
flake8 = [
|
flake8 = [
|
||||||
{file = "flake8-3.8.3-py2.py3-none-any.whl", hash = "sha256:15e351d19611c887e482fb960eae4d44845013cc142d42896e9862f775d8cf5c"},
|
{file = "flake8-3.8.3-py2.py3-none-any.whl", hash = "sha256:15e351d19611c887e482fb960eae4d44845013cc142d42896e9862f775d8cf5c"},
|
||||||
{file = "flake8-3.8.3.tar.gz", hash = "sha256:f04b9fcbac03b0a3e58c0ab3a0ecc462e023a9faf046d57794184028123aa208"},
|
{file = "flake8-3.8.3.tar.gz", hash = "sha256:f04b9fcbac03b0a3e58c0ab3a0ecc462e023a9faf046d57794184028123aa208"},
|
||||||
@ -1174,6 +1234,10 @@ flask = [
|
|||||||
flask-bcrypt = [
|
flask-bcrypt = [
|
||||||
{file = "Flask-Bcrypt-0.7.1.tar.gz", hash = "sha256:d71c8585b2ee1c62024392ebdbc447438564e2c8c02b4e57b56a4cafd8d13c5f"},
|
{file = "Flask-Bcrypt-0.7.1.tar.gz", hash = "sha256:d71c8585b2ee1c62024392ebdbc447438564e2c8c02b4e57b56a4cafd8d13c5f"},
|
||||||
]
|
]
|
||||||
|
flask-dramatiq = [
|
||||||
|
{file = "flask-dramatiq-0.6.0.tar.gz", hash = "sha256:63709e73d7c8d2e5d9bc554d1e859d91c5c5c9a4ebc9461752655bf1e0b87420"},
|
||||||
|
{file = "flask_dramatiq-0.6.0-py3-none-any.whl", hash = "sha256:7d4a9289721577f726183f7c44c6713a16bbdff54b946f27abc2ffcc65768adf"},
|
||||||
|
]
|
||||||
flask-migrate = [
|
flask-migrate = [
|
||||||
{file = "Flask-Migrate-2.5.3.tar.gz", hash = "sha256:a69d508c2e09d289f6e55a417b3b8c7bfe70e640f53d2d9deb0d056a384f37ee"},
|
{file = "Flask-Migrate-2.5.3.tar.gz", hash = "sha256:a69d508c2e09d289f6e55a417b3b8c7bfe70e640f53d2d9deb0d056a384f37ee"},
|
||||||
{file = "Flask_Migrate-2.5.3-py2.py3-none-any.whl", hash = "sha256:4dc4a5cce8cbbb06b8dc963fd86cf8136bd7d875aabe2d840302ea739b243732"},
|
{file = "Flask_Migrate-2.5.3-py2.py3-none-any.whl", hash = "sha256:4dc4a5cce8cbbb06b8dc963fd86cf8136bd7d875aabe2d840302ea739b243732"},
|
||||||
@ -1308,6 +1372,10 @@ pluggy = [
|
|||||||
{file = "pluggy-0.13.1-py2.py3-none-any.whl", hash = "sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d"},
|
{file = "pluggy-0.13.1-py2.py3-none-any.whl", hash = "sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d"},
|
||||||
{file = "pluggy-0.13.1.tar.gz", hash = "sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0"},
|
{file = "pluggy-0.13.1.tar.gz", hash = "sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0"},
|
||||||
]
|
]
|
||||||
|
prometheus-client = [
|
||||||
|
{file = "prometheus_client-0.8.0-py2.py3-none-any.whl", hash = "sha256:983c7ac4b47478720db338f1491ef67a100b474e3bc7dafcbaefb7d0b8f9b01c"},
|
||||||
|
{file = "prometheus_client-0.8.0.tar.gz", hash = "sha256:c6e6b706833a6bd1fd51711299edee907857be10ece535126a158f911ee80915"},
|
||||||
|
]
|
||||||
psycopg2-binary = [
|
psycopg2-binary = [
|
||||||
{file = "psycopg2-binary-2.8.5.tar.gz", hash = "sha256:ccdc6a87f32b491129ada4b87a43b1895cf2c20fdb7f98ad979647506ffc41b6"},
|
{file = "psycopg2-binary-2.8.5.tar.gz", hash = "sha256:ccdc6a87f32b491129ada4b87a43b1895cf2c20fdb7f98ad979647506ffc41b6"},
|
||||||
{file = "psycopg2_binary-2.8.5-cp27-cp27m-macosx_10_6_intel.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:96d3038f5bd061401996614f65d27a4ecb62d843eb4f48e212e6d129171a721f"},
|
{file = "psycopg2_binary-2.8.5-cp27-cp27m-macosx_10_6_intel.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:96d3038f5bd061401996614f65d27a4ecb62d843eb4f48e212e6d129171a721f"},
|
||||||
@ -1417,6 +1485,10 @@ recommonmark = [
|
|||||||
{file = "recommonmark-0.6.0-py2.py3-none-any.whl", hash = "sha256:2ec4207a574289355d5b6ae4ae4abb29043346ca12cdd5f07d374dc5987d2852"},
|
{file = "recommonmark-0.6.0-py2.py3-none-any.whl", hash = "sha256:2ec4207a574289355d5b6ae4ae4abb29043346ca12cdd5f07d374dc5987d2852"},
|
||||||
{file = "recommonmark-0.6.0.tar.gz", hash = "sha256:29cd4faeb6c5268c633634f2d69aef9431e0f4d347f90659fd0aab20e541efeb"},
|
{file = "recommonmark-0.6.0.tar.gz", hash = "sha256:29cd4faeb6c5268c633634f2d69aef9431e0f4d347f90659fd0aab20e541efeb"},
|
||||||
]
|
]
|
||||||
|
redis = [
|
||||||
|
{file = "redis-3.5.3-py2.py3-none-any.whl", hash = "sha256:432b788c4530cfe16d8d943a09d40ca6c16149727e4afe8c2c9d5580c59d9f24"},
|
||||||
|
{file = "redis-3.5.3.tar.gz", hash = "sha256:0e7e0cfca8660dea8b7d5cd8c4f6c5e29e11f31158c0b0ae91a397f00e5a05a2"},
|
||||||
|
]
|
||||||
regex = [
|
regex = [
|
||||||
{file = "regex-2020.6.8-cp27-cp27m-win32.whl", hash = "sha256:fbff901c54c22425a5b809b914a3bfaf4b9570eee0e5ce8186ac71eb2025191c"},
|
{file = "regex-2020.6.8-cp27-cp27m-win32.whl", hash = "sha256:fbff901c54c22425a5b809b914a3bfaf4b9570eee0e5ce8186ac71eb2025191c"},
|
||||||
{file = "regex-2020.6.8-cp27-cp27m-win_amd64.whl", hash = "sha256:112e34adf95e45158c597feea65d06a8124898bdeac975c9087fe71b572bd938"},
|
{file = "regex-2020.6.8-cp27-cp27m-win_amd64.whl", hash = "sha256:112e34adf95e45158c597feea65d06a8124898bdeac975c9087fe71b572bd938"},
|
||||||
|
@ -19,6 +19,8 @@ python-forecastio = "^1.4"
|
|||||||
gunicorn = "^20.0"
|
gunicorn = "^20.0"
|
||||||
tqdm = "^4.42"
|
tqdm = "^4.42"
|
||||||
humanize = "^2.5.0"
|
humanize = "^2.5.0"
|
||||||
|
dramatiq = {extras = ["redis"], version = "^1.9.0"}
|
||||||
|
flask-dramatiq = "^0.6.0"
|
||||||
|
|
||||||
[tool.poetry.dev-dependencies]
|
[tool.poetry.dev-dependencies]
|
||||||
black = "^19.10b0"
|
black = "^19.10b0"
|
||||||
|
Loading…
Reference in New Issue
Block a user