diff --git a/.env.sample b/.env.sample index 028c6b2..8f774e9 100644 --- a/.env.sample +++ b/.env.sample @@ -4,6 +4,8 @@ POSTGRES_DB_PORT=5432 POSTGRES_USER=admin POSTGRES_PASSWORD=some_password POSTGRES_HOST=db +# Redis +REDIS_URL=redis:6379/0 # pgAdmin PGADMIN_DEFAULT_EMAIL=admin@gmail.com PGADMIN_DEFAULT_PASSWORD=admin @@ -25,4 +27,8 @@ MINIO_ROOT_USER=minioadmin MINIO_ROOT_PASSWORD=some_password MINIO_HOST=minio MINIO_PORT=9000 -MINIO_STORAGE=cinema-storage \ No newline at end of file +MINIO_STORAGE=cinema-storage +# Stripe +STRIPE_PUBLIC_KEY=pk_test_51QdLelI3GzuBsZIuMFIM4tX6GaY61I8nVGfRv7gavDisNoIRZMcDSGZqJQ2E4mgBaBHBuOE0dC3W3IJav8tcGFaO00B5emcRb2 +STRIPE_SECRET_KEY=sk_test_51QdLelI3GzuBsZIu8XZOaSEzdEtKP8nfbeYlECcuOkrXykXnhNgQy0zede1r8BZM39nKtNBnAUs1FAwQsopJTeTq00wbLcZc0V +STRIPE_WEBHOOK_SECRET=whsec_34eb6a6b355e66061398d65b05a2a39a0ac17df56cdbe4ca0e8a83993dddda4e diff --git a/docker-compose-dev.yml b/docker-compose-dev.yml index 799757f..f199818 100644 --- a/docker-compose-dev.yml +++ b/docker-compose-dev.yml @@ -117,6 +117,54 @@ services: networks: - cinema_network + redis: + image: redis:alpine + healthcheck: + test: [ "CMD", "redis-cli", "ping" ] + interval: 10s + timeout: 5s + retries: 5 + networks: + - cinema_network + + celery: + build: + context: . + command: celery -A services.celery_app worker --loglevel=info + volumes: + - celery_data:/data + env_file: + - .env + environment: + - PYTHONPATH=/usr/src/fastapi + depends_on: + redis: + condition: service_healthy + db: + condition: service_healthy + restart: always + networks: + - cinema_network + + celery-beat: + build: + context: . + command: celery -A services.celery_app beat --loglevel=info --scheduler celery.beat.PersistentScheduler + volumes: + - celery_data:/data + env_file: + - .env + environment: + - PYTHONPATH=/usr/src/fastapi + depends_on: + redis: + condition: service_healthy + db: + condition: service_healthy + restart: always + networks: + - cinema_network + volumes: postgres_cinema_data: driver: local @@ -124,6 +172,8 @@ volumes: driver: local minio_data: driver: local + celery_data: + driver: local networks: cinema_network: diff --git a/poetry.lock b/poetry.lock index 0fda3ba..54e7059 100644 --- a/poetry.lock +++ b/poetry.lock @@ -21,6 +21,22 @@ typing-extensions = ">=4" [package.extras] tz = ["backports.zoneinfo", "tzdata"] +[[package]] +name = "amqp" +version = "5.3.1" +description = "Low-level AMQP client for Python (fork of amqplib)." +optional = false +python-versions = ">=3.6" +groups = ["main"] +markers = "python_version <= \"3.11\" or python_version >= \"3.12\"" +files = [ + {file = "amqp-5.3.1-py3-none-any.whl", hash = "sha256:43b3319e1b4e7d1251833a93d672b4af1e40f3d632d479b98661a95f117880a2"}, + {file = "amqp-5.3.1.tar.gz", hash = "sha256:cddc00c725449522023bad949f70fff7b48f0b1ade74d170a6f10ab044739432"}, +] + +[package.dependencies] +vine = ">=5.0.0,<6.0.0" + [[package]] name = "annotated-types" version = "0.7.0" @@ -58,6 +74,19 @@ doc = ["Sphinx (>=7.4,<8.0)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", test = ["anyio[trio]", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "trustme", "truststore (>=0.9.1)", "uvloop (>=0.21)"] trio = ["trio (>=0.26.1)"] +[[package]] +name = "async-timeout" +version = "5.0.1" +description = "Timeout context manager for asyncio programs" +optional = false +python-versions = ">=3.8" +groups = ["main"] +markers = "python_full_version < \"3.11.3\"" +files = [ + {file = "async_timeout-5.0.1-py3-none-any.whl", hash = "sha256:39e3809566ff85354557ec2398b55e096c8364bacac9405a7a1fa429e77fe76c"}, + {file = "async_timeout-5.0.1.tar.gz", hash = "sha256:d9321a7a3d5a6a5e187e824d2fa0793ce379a202935782d555d6e9d2735677d3"}, +] + [[package]] name = "bcrypt" version = "4.0.1" @@ -96,19 +125,20 @@ typecheck = ["mypy"] [[package]] name = "beautifulsoup4" -version = "4.12.3" +version = "4.13.1" description = "Screen-scraping library" optional = false -python-versions = ">=3.6.0" +python-versions = ">=3.7.0" groups = ["main"] markers = "python_version <= \"3.11\" or python_version >= \"3.12\"" files = [ - {file = "beautifulsoup4-4.12.3-py3-none-any.whl", hash = "sha256:b80878c9f40111313e55da8ba20bdba06d8fa3969fc68304167741bbf9e082ed"}, - {file = "beautifulsoup4-4.12.3.tar.gz", hash = "sha256:74e3d1928edc070d21748185c46e3fb33490f22f52a3addee9aee0f4f7781051"}, + {file = "beautifulsoup4-4.13.1-py3-none-any.whl", hash = "sha256:72465267014897bb10ca749bb632bde6c2d20f3254afd5458544bd74e6c2e6d8"}, + {file = "beautifulsoup4-4.13.1.tar.gz", hash = "sha256:741c8b6903a1e4ae8ba32b9c9ae7510dab7a197fdbadcf9fcdeb0891ef5ec66a"}, ] [package.dependencies] soupsieve = ">1.2" +typing-extensions = ">=4.0.0" [package.extras] cchardet = ["cchardet"] @@ -117,6 +147,19 @@ charset-normalizer = ["charset-normalizer"] html5lib = ["html5lib"] lxml = ["lxml"] +[[package]] +name = "billiard" +version = "4.2.1" +description = "Python multiprocessing fork with improvements and bugfixes" +optional = false +python-versions = ">=3.7" +groups = ["main"] +markers = "python_version <= \"3.11\" or python_version >= \"3.12\"" +files = [ + {file = "billiard-4.2.1-py3-none-any.whl", hash = "sha256:40b59a4ac8806ba2c2369ea98d876bc6108b051c227baffd928c644d15d8f3cb"}, + {file = "billiard-4.2.1.tar.gz", hash = "sha256:12b641b0c539073fc8d3f5b8b7be998956665c4233c7c1fcd66a7e677c4fb36f"}, +] + [[package]] name = "black" version = "25.1.0" @@ -167,19 +210,19 @@ uvloop = ["uvloop (>=0.15.2)"] [[package]] name = "boto3" -version = "1.36.10" +version = "1.36.12" description = "The AWS SDK for Python" optional = false python-versions = ">=3.8" groups = ["main"] markers = "python_version <= \"3.11\" or python_version >= \"3.12\"" files = [ - {file = "boto3-1.36.10-py3-none-any.whl", hash = "sha256:5f8d5c2024a2d1411d3d67abb7357ec7d4c6162e3f1c396dc9b79d042cfd0a80"}, - {file = "boto3-1.36.10.tar.gz", hash = "sha256:d2f1010db699326b26fbd465d91c4b49177c9d995d7e72c0f31335f139efa0d2"}, + {file = "boto3-1.36.12-py3-none-any.whl", hash = "sha256:32cdf0967287f3ec25a9dc09df0d29cb86b8900c3e0546a63d672775d8127abf"}, + {file = "boto3-1.36.12.tar.gz", hash = "sha256:287d84f49bba3255a17b374578127d42b6251e72f55914a62e0ad9ca78c0954b"}, ] [package.dependencies] -botocore = ">=1.36.10,<1.37.0" +botocore = ">=1.36.12,<1.37.0" jmespath = ">=0.7.1,<2.0.0" s3transfer = ">=0.11.0,<0.12.0" @@ -188,15 +231,15 @@ crt = ["botocore[crt] (>=1.21.0,<2.0a0)"] [[package]] name = "botocore" -version = "1.36.10" +version = "1.36.12" description = "Low-level, data-driven core of boto 3." optional = false python-versions = ">=3.8" groups = ["main"] markers = "python_version <= \"3.11\" or python_version >= \"3.12\"" files = [ - {file = "botocore-1.36.10-py3-none-any.whl", hash = "sha256:45c8a6e01dc18d44a13ba688f1d60ad562db8154b08c46b412387ea040a741c2"}, - {file = "botocore-1.36.10.tar.gz", hash = "sha256:d27bb73f0ea81395527a6298ac0a7ea5b2958094169f7cf7d48e3f4e4bc21b65"}, + {file = "botocore-1.36.12-py3-none-any.whl", hash = "sha256:5ae1ed362c8ed908a6ced8cdd12b21e2196c100bc79f9e95c9c1fc7f9ea74f5a"}, + {file = "botocore-1.36.12.tar.gz", hash = "sha256:86ed88beb4f244c96529435c868d3940073c2774116f0023fb7691f6e7053bd9"}, ] [package.dependencies] @@ -207,6 +250,64 @@ urllib3 = {version = ">=1.25.4,<2.2.0 || >2.2.0,<3", markers = "python_version > [package.extras] crt = ["awscrt (==0.23.8)"] +[[package]] +name = "celery" +version = "5.4.0" +description = "Distributed Task Queue." +optional = false +python-versions = ">=3.8" +groups = ["main"] +markers = "python_version <= \"3.11\" or python_version >= \"3.12\"" +files = [ + {file = "celery-5.4.0-py3-none-any.whl", hash = "sha256:369631eb580cf8c51a82721ec538684994f8277637edde2dfc0dacd73ed97f64"}, + {file = "celery-5.4.0.tar.gz", hash = "sha256:504a19140e8d3029d5acad88330c541d4c3f64c789d85f94756762d8bca7e706"}, +] + +[package.dependencies] +billiard = ">=4.2.0,<5.0" +click = ">=8.1.2,<9.0" +click-didyoumean = ">=0.3.0" +click-plugins = ">=1.1.1" +click-repl = ">=0.2.0" +kombu = ">=5.3.4,<6.0" +python-dateutil = ">=2.8.2" +tzdata = ">=2022.7" +vine = ">=5.1.0,<6.0" + +[package.extras] +arangodb = ["pyArango (>=2.0.2)"] +auth = ["cryptography (==42.0.5)"] +azureblockblob = ["azure-storage-blob (>=12.15.0)"] +brotli = ["brotli (>=1.0.0)", "brotlipy (>=0.7.0)"] +cassandra = ["cassandra-driver (>=3.25.0,<4)"] +consul = ["python-consul2 (==0.1.5)"] +cosmosdbsql = ["pydocumentdb (==2.3.5)"] +couchbase = ["couchbase (>=3.0.0)"] +couchdb = ["pycouchdb (==1.14.2)"] +django = ["Django (>=2.2.28)"] +dynamodb = ["boto3 (>=1.26.143)"] +elasticsearch = ["elastic-transport (<=8.13.0)", "elasticsearch (<=8.13.0)"] +eventlet = ["eventlet (>=0.32.0)"] +gcs = ["google-cloud-storage (>=2.10.0)"] +gevent = ["gevent (>=1.5.0)"] +librabbitmq = ["librabbitmq (>=2.0.0)"] +memcache = ["pylibmc (==1.6.3)"] +mongodb = ["pymongo[srv] (>=4.0.2)"] +msgpack = ["msgpack (==1.0.8)"] +pymemcache = ["python-memcached (>=1.61)"] +pyro = ["pyro4 (==4.82)"] +pytest = ["pytest-celery[all] (>=1.0.0)"] +redis = ["redis (>=4.5.2,!=4.5.5,<6.0.0)"] +s3 = ["boto3 (>=1.26.143)"] +slmq = ["softlayer-messaging (>=1.0.3)"] +solar = ["ephem (==4.1.5)"] +sqlalchemy = ["sqlalchemy (>=1.4.48,<2.1)"] +sqs = ["boto3 (>=1.26.143)", "kombu[sqs] (>=5.3.4)", "pycurl (>=7.43.0.5)", "urllib3 (>=1.26.16)"] +tblib = ["tblib (>=1.3.0)", "tblib (>=1.5.0)"] +yaml = ["PyYAML (>=3.10)"] +zookeeper = ["kazoo (>=1.3.1)"] +zstd = ["zstandard (==0.22.0)"] + [[package]] name = "certifi" version = "2025.1.31" @@ -420,6 +521,61 @@ files = [ [package.dependencies] colorama = {version = "*", markers = "platform_system == \"Windows\""} +[[package]] +name = "click-didyoumean" +version = "0.3.1" +description = "Enables git-like *did-you-mean* feature in click" +optional = false +python-versions = ">=3.6.2" +groups = ["main"] +markers = "python_version <= \"3.11\" or python_version >= \"3.12\"" +files = [ + {file = "click_didyoumean-0.3.1-py3-none-any.whl", hash = "sha256:5c4bb6007cfea5f2fd6583a2fb6701a22a41eb98957e63d0fac41c10e7c3117c"}, + {file = "click_didyoumean-0.3.1.tar.gz", hash = "sha256:4f82fdff0dbe64ef8ab2279bd6aa3f6a99c3b28c05aa09cbfc07c9d7fbb5a463"}, +] + +[package.dependencies] +click = ">=7" + +[[package]] +name = "click-plugins" +version = "1.1.1" +description = "An extension module for click to enable registering CLI commands via setuptools entry-points." +optional = false +python-versions = "*" +groups = ["main"] +markers = "python_version <= \"3.11\" or python_version >= \"3.12\"" +files = [ + {file = "click-plugins-1.1.1.tar.gz", hash = "sha256:46ab999744a9d831159c3411bb0c79346d94a444df9a3a3742e9ed63645f264b"}, + {file = "click_plugins-1.1.1-py2.py3-none-any.whl", hash = "sha256:5d262006d3222f5057fd81e1623d4443e41dcda5dc815c06b442aa3c02889fc8"}, +] + +[package.dependencies] +click = ">=4.0" + +[package.extras] +dev = ["coveralls", "pytest (>=3.6)", "pytest-cov", "wheel"] + +[[package]] +name = "click-repl" +version = "0.3.0" +description = "REPL plugin for Click" +optional = false +python-versions = ">=3.6" +groups = ["main"] +markers = "python_version <= \"3.11\" or python_version >= \"3.12\"" +files = [ + {file = "click-repl-0.3.0.tar.gz", hash = "sha256:17849c23dba3d667247dc4defe1757fff98694e90fe37474f3feebb69ced26a9"}, + {file = "click_repl-0.3.0-py3-none-any.whl", hash = "sha256:fb7e06deb8da8de86180a33a9da97ac316751c094c6899382da7feeeeb51b812"}, +] + +[package.dependencies] +click = ">=7.0" +prompt-toolkit = ">=3.0.36" + +[package.extras] +testing = ["pytest (>=7.2.1)", "pytest-cov (>=4.0.0)", "tox (>=4.4.3)"] + [[package]] name = "colorama" version = "0.4.6" @@ -867,6 +1023,41 @@ files = [ {file = "jmespath-1.0.1.tar.gz", hash = "sha256:90261b206d6defd58fdd5e85f478bf633a2901798906be2ad389150c5c60edbe"}, ] +[[package]] +name = "kombu" +version = "5.4.2" +description = "Messaging library for Python." +optional = false +python-versions = ">=3.8" +groups = ["main"] +markers = "python_version <= \"3.11\" or python_version >= \"3.12\"" +files = [ + {file = "kombu-5.4.2-py3-none-any.whl", hash = "sha256:14212f5ccf022fc0a70453bb025a1dcc32782a588c49ea866884047d66e14763"}, + {file = "kombu-5.4.2.tar.gz", hash = "sha256:eef572dd2fd9fc614b37580e3caeafdd5af46c1eff31e7fba89138cdb406f2cf"}, +] + +[package.dependencies] +amqp = ">=5.1.1,<6.0.0" +tzdata = {version = "*", markers = "python_version >= \"3.9\""} +vine = "5.1.0" + +[package.extras] +azureservicebus = ["azure-servicebus (>=7.10.0)"] +azurestoragequeues = ["azure-identity (>=1.12.0)", "azure-storage-queue (>=12.6.0)"] +confluentkafka = ["confluent-kafka (>=2.2.0)"] +consul = ["python-consul2 (==0.1.5)"] +librabbitmq = ["librabbitmq (>=2.0.0)"] +mongodb = ["pymongo (>=4.1.1)"] +msgpack = ["msgpack (==1.1.0)"] +pyro = ["pyro4 (==4.82)"] +qpid = ["qpid-python (>=0.26)", "qpid-tools (>=0.26)"] +redis = ["redis (>=4.5.2,!=4.5.5,!=5.0.2)"] +slmq = ["softlayer-messaging (>=1.0.3)"] +sqlalchemy = ["sqlalchemy (>=1.4.48,<2.1)"] +sqs = ["boto3 (>=1.26.143)", "pycurl (>=7.43.0.5)", "urllib3 (>=1.26.16)"] +yaml = ["PyYAML (>=3.10)"] +zookeeper = ["kazoo (>=2.8.0)"] + [[package]] name = "mako" version = "1.3.8" @@ -1374,6 +1565,22 @@ files = [ dev = ["pre-commit", "tox"] testing = ["pytest", "pytest-benchmark"] +[[package]] +name = "prompt-toolkit" +version = "3.0.50" +description = "Library for building powerful interactive command lines in Python" +optional = false +python-versions = ">=3.8.0" +groups = ["main"] +markers = "python_version <= \"3.11\" or python_version >= \"3.12\"" +files = [ + {file = "prompt_toolkit-3.0.50-py3-none-any.whl", hash = "sha256:9b6427eb19e479d98acff65196a307c555eb567989e6d88ebbb1b509d9779198"}, + {file = "prompt_toolkit-3.0.50.tar.gz", hash = "sha256:544748f3860a2623ca5cd6d2795e7a14f3d0e1c3c9728359013f79877fc89bab"}, +] + +[package.dependencies] +wcwidth = "*" + [[package]] name = "psycopg2-binary" version = "2.9.10" @@ -1805,6 +2012,26 @@ files = [ {file = "pytz-2025.1.tar.gz", hash = "sha256:c2db42be2a2518b28e65f9207c4d05e6ff547d1efa4086469ef855e4ab70178e"}, ] +[[package]] +name = "redis" +version = "5.2.1" +description = "Python client for Redis database and key-value store" +optional = false +python-versions = ">=3.8" +groups = ["main"] +markers = "python_version <= \"3.11\" or python_version >= \"3.12\"" +files = [ + {file = "redis-5.2.1-py3-none-any.whl", hash = "sha256:ee7e1056b9aea0f04c6c2ed59452947f34c4940ee025f5dd83e6a6418b6989e4"}, + {file = "redis-5.2.1.tar.gz", hash = "sha256:16f2e22dff21d5125e8481515e386711a34cbec50f0e44413dd7d9c060a54e0f"}, +] + +[package.dependencies] +async-timeout = {version = ">=4.0.3", markers = "python_full_version < \"3.11.3\""} + +[package.extras] +hiredis = ["hiredis (>=3.0.0)"] +ocsp = ["cryptography (>=36.0.1)", "pyopenssl (==23.2.1)", "requests (>=2.31.0)"] + [[package]] name = "requests" version = "2.32.3" @@ -2212,7 +2439,33 @@ files = [ [package.extras] crypto-eth-addresses = ["eth-hash[pycryptodome] (>=0.7.0)"] +[[package]] +name = "vine" +version = "5.1.0" +description = "Python promises." +optional = false +python-versions = ">=3.6" +groups = ["main"] +markers = "python_version <= \"3.11\" or python_version >= \"3.12\"" +files = [ + {file = "vine-5.1.0-py3-none-any.whl", hash = "sha256:40fdf3c48b2cfe1c38a49e9ae2da6fda88e4794c810050a728bd7413811fb1dc"}, + {file = "vine-5.1.0.tar.gz", hash = "sha256:8b62e981d35c41049211cf62a0a1242d8c1ee9bd15bb196ce38aefd6799e61e0"}, +] + +[[package]] +name = "wcwidth" +version = "0.2.13" +description = "Measures the displayed width of unicode strings in a terminal" +optional = false +python-versions = "*" +groups = ["main"] +markers = "python_version <= \"3.11\" or python_version >= \"3.12\"" +files = [ + {file = "wcwidth-0.2.13-py2.py3-none-any.whl", hash = "sha256:3da69048e4540d84af32131829ff948f1e022c1c6bdb8d6102117aac784f6859"}, + {file = "wcwidth-0.2.13.tar.gz", hash = "sha256:72ea0c06399eb286d978fdedb6923a9eb47e1c486ce63e9b4e64fc18303972b5"}, +] + [metadata] lock-version = "2.1" python-versions = "^3.10" -content-hash = "12c79db7da6f99ba5506b7ae9a81b2f71dc6a778e0459791a36a788b5310dfbd" +content-hash = "b638b00ace34ab5493d225cd82e5fe3bd12bc749125d91fb4ccb2a4979c33fc0" diff --git a/pyproject.toml b/pyproject.toml index c0d15e7..280382e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -40,6 +40,8 @@ black = "^25.1.0" stripe = "^11.5.0" mypy = "^1.14.1" fastapi-pagination = "^0.12.34" +celery = "^5.4.0" +redis = "^5.2.1" [tool.poetry.group.dev.dependencies] diff --git a/src/config/settings.py b/src/config/settings.py index 8c5d5ef..30c73c2 100644 --- a/src/config/settings.py +++ b/src/config/settings.py @@ -41,6 +41,8 @@ class BaseAppSettings(BaseSettings): STRIPE_PUBLIC_KEY: str = os.environ["STRIPE_PUBLIC_KEY"] STRIPE_WEBHOOK_SECRET: str = os.environ["STRIPE_WEBHOOK_SECRET"] + REDIS_URL: str = os.environ["REDIS_URL"] + @property def S3_STORAGE_ENDPOINT(self) -> str: return f"http://{self.S3_STORAGE_HOST}:{self.S3_STORAGE_PORT}" diff --git a/src/database/crud/accounts.py b/src/database/crud/accounts.py index b9f5757..ce08323 100644 --- a/src/database/crud/accounts.py +++ b/src/database/crud/accounts.py @@ -106,3 +106,12 @@ def create_refresh_token_by_user_id_days_token( def get_refresh_token_by_refresh_token(db: Session, refresh_token: Any) -> Query | None: return db.query(RefreshToken).filter_by(token=refresh_token).first() + + +def get_all_activation_tokens(db: Session) -> Query: + return db.query(ActivationToken).all() + + +def remove_activation_token(db: Session, token: Any) -> None: + db.delete(token) + db.commit() diff --git a/src/routes/accounts.py b/src/routes/accounts.py index c2440bc..aba4ed0 100644 --- a/src/routes/accounts.py +++ b/src/routes/accounts.py @@ -13,7 +13,7 @@ ) from database import ( UserGroupEnum, - get_db, + get_db, ActivationToken, ) from database.crud.accounts import ( create_password_reset_token_by_user_id, @@ -28,8 +28,9 @@ get_refresh_token_by_refresh_token, get_user_by_email, get_user_by_id, - get_user_group_by_name, + get_user_group_by_name, get_all_activation_tokens, remove_activation_token, ) +from database.session_postgresql import get_postgresql_db, PostgresqlSessionLocal from exceptions import BaseSecurityError from notifications import EmailSenderInterface from schemas.accounts import ( diff --git a/src/services/celery_app.py b/src/services/celery_app.py new file mode 100644 index 0000000..8dcb5a0 --- /dev/null +++ b/src/services/celery_app.py @@ -0,0 +1,20 @@ +from celery import Celery + +from config import get_settings + +settings = get_settings() +redis_url = settings.REDIS_URL + +celery_app = Celery( + "celery_app", + broker="redis://redis:6379/0", + backend="redis://redis:6379/0", + include=["services.tasks"], +) + +celery_app.conf.beat_schedule = { + 'review_activation_token-every-day': { + 'task': 'services.tasks.review_activation_token', + 'schedule': 30.0, + }, +} diff --git a/src/services/tasks.py b/src/services/tasks.py new file mode 100644 index 0000000..0d45ab3 --- /dev/null +++ b/src/services/tasks.py @@ -0,0 +1,21 @@ +from datetime import datetime, timezone + +from database.crud.accounts import get_all_activation_tokens, remove_activation_token +from database.session_postgresql import get_postgresql_db +from services.celery_app import celery_app +import logging + +logger = logging.getLogger(__name__) + +@celery_app.task +def review_activation_token() -> None: + db = next(get_postgresql_db()) + try: + activation_tokens = get_all_activation_tokens(db) + for activation_token in activation_tokens: + if (activation_token.expires_at.replace(tzinfo=timezone.utc) < + datetime.now(timezone.utc)): + remove_activation_token(db=db, activation_token=activation_token.token) + finally: + db.close() + logger.info("Remove expired token completed")