├── .example.env ├── .gitignore ├── CONTRIBUTING.md ├── Dockerfile ├── LICENSE ├── Pipfile ├── Pipfile.lock ├── README.md ├── alembic.ini ├── alembic ├── README ├── env.py └── script.py.mako ├── app ├── base.py ├── db.py ├── models.py ├── routes.py ├── schema.py ├── services.py └── settings.py ├── auth ├── api.py ├── exceptions.py ├── models.py ├── schema.py ├── services.py └── tests │ └── test_auth_api.py ├── docker-compose.yml └── main.py /.example.env: -------------------------------------------------------------------------------- 1 | ALLOWED_HOST=0.0.0.0 2 | SECRET_KEY=meandyouaretogether 3 | DEBUG=True 4 | PORT=11000 5 | POSTGRES_USER=sasori 6 | POSTGRES_PASSWORD=sasori 7 | POSTGRES_DB=akatsuki 8 | POSTGRES_TEST_DB=hebi 9 | POSTGRES_PORT=5432 10 | POSTGRES_HOST=postgres 11 | REDIS_HOST=redis 12 | REDIS_PORT=6379 -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | pip-wheel-metadata/ 24 | share/python-wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | MANIFEST 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .nox/ 44 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | *.py,cover 51 | .hypothesis/ 52 | .pytest_cache/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | target/ 76 | 77 | # Jupyter Notebook 78 | .ipynb_checkpoints 79 | 80 | # IPython 81 | profile_default/ 82 | ipython_config.py 83 | 84 | # pyenv 85 | .python-version 86 | 87 | # pipenv 88 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 89 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 90 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 91 | # install all needed dependencies. 92 | #Pipfile.lock 93 | 94 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 95 | __pypackages__/ 96 | 97 | # Celery stuff 98 | celerybeat-schedule 99 | celerybeat.pid 100 | 101 | # SageMath parsed files 102 | *.sage.py 103 | 104 | # Environments 105 | .env 106 | .venv 107 | env/ 108 | venv/ 109 | ENV/ 110 | env.bak/ 111 | venv.bak/ 112 | 113 | # Spyder project settings 114 | .spyderproject 115 | .spyproject 116 | 117 | # Rope project settings 118 | .ropeproject 119 | 120 | # mkdocs documentation 121 | /site 122 | 123 | # mypy 124 | .mypy_cache/ 125 | .dmypy.json 126 | dmypy.json 127 | 128 | # Pyre type checker 129 | .pyre/ 130 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to zis FastAPI template 2 | So, we'll go straight to the point. Intros are for weak folks. 3 | 4 | ## Wait first... 5 | I, the creator of this repository, am the 6 | [Benevolent Dictator For Life](http://en.wikipedia.org/wiki/Benevolent_Dictator_for_Life) 7 | as it pertains to this project. This is actually important as you will see. 8 | I'll call it the BDFL. 9 | 10 | ## How do I contribute? 11 | It starts from issues first. 12 | Issues are created and if vetted by the BDFL (for now), 13 | you can go on to create a pull request for it. 14 | The name of the PR branches are very important and should follow a pattern: 15 | - If it is a new feature: should be named: f-\ 16 | - If it is a bug: should be named: b-\ 17 | - Anything else: should be named: e-\ 18 | 19 | ## FAQs 20 | Everything needs a FAQ. 21 | 1. "I think we need to add a new feature!" : 22 | Yeah, create an issue for it and everyone will talk about it 23 | 2. "These rules are dumb and should be better" : 24 | Yeah, create an issue for it and everyone will talk about it 25 | 3. "I hate this project" : 26 | Nice, [send me a mail](mailto:adeoti.15.jude@gmail.com) and we'll 27 | talk about it. 28 | 29 | Danke! 🔥 30 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # pull official base image 2 | FROM python:3.9-slim-bullseye 3 | 4 | # set work directory 5 | WORKDIR /usr/src/app 6 | 7 | # set environment variables 8 | ENV PYTHONDONTWRITEBYTECODE 1 9 | ENV PYTHONUNBUFFERED 1 10 | 11 | # install necessary packages 12 | RUN apt-get update \ 13 | && apt-get -y install build-essential \ 14 | && apt-get -y install libpq-dev gcc \ 15 | && apt-get -y install netcat 16 | 17 | COPY . . 18 | # install pipenv and project dependencies 19 | RUN pip install pipenv 20 | RUN pipenv install --system --deploy --ignore-pipfile -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Lord_Sarcastic 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Pipfile: -------------------------------------------------------------------------------- 1 | [[source]] 2 | url = "https://pypi.org/simple" 3 | verify_ssl = true 4 | name = "pypi" 5 | 6 | [packages] 7 | fastapi = {extras = ["all"], version = "*"} 8 | alembic = "*" 9 | psycopg2 = "*" 10 | databases = {extras = ["postgresql"], version = "*"} 11 | asyncpg = "*" 12 | python-jose = {extras = ["cryptography"], version = "*"} 13 | passlib = {extras = ["bcrypt"], version = "*"} 14 | python-multipart = "*" 15 | pytest = "*" 16 | sqlalchemy-utils = "*" 17 | aioredis = "*" 18 | 19 | [dev-packages] 20 | 21 | [requires] 22 | python_version = "3.9" 23 | -------------------------------------------------------------------------------- /Pipfile.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "hash": { 4 | "sha256": "5ea71bf880ceda7ec61566248e98e1686c914b82e68a968e7198d8e729cb7bb1" 5 | }, 6 | "pipfile-spec": 6, 7 | "requires": { 8 | "python_version": "3.9" 9 | }, 10 | "sources": [ 11 | { 12 | "name": "pypi", 13 | "url": "https://pypi.org/simple", 14 | "verify_ssl": true 15 | } 16 | ] 17 | }, 18 | "default": { 19 | "aioredis": { 20 | "hashes": [ 21 | "sha256:9ac0d0b3b485d293b8ca1987e6de8658d7dafcca1cddfcd1d506cae8cdebfdd6", 22 | "sha256:eaa51aaf993f2d71f54b70527c440437ba65340588afeb786cd87c55c89cd98e" 23 | ], 24 | "index": "pypi", 25 | "version": "==2.0.1" 26 | }, 27 | "alembic": { 28 | "hashes": [ 29 | "sha256:0a024d7f2de88d738d7395ff866997314c837be6104e90c5724350313dee4da4", 30 | "sha256:cd0b5e45b14b706426b833f06369b9a6d5ee03f826ec3238723ce8caaf6e5ffa" 31 | ], 32 | "index": "pypi", 33 | "version": "==1.8.1" 34 | }, 35 | "anyio": { 36 | "hashes": [ 37 | "sha256:413adf95f93886e442aea925f3ee43baa5a765a64a0f52c6081894f9992fdd0b", 38 | "sha256:cb29b9c70620506a9a8f87a309591713446953302d7d995344d0d7c6c0c9a7be" 39 | ], 40 | "markers": "python_full_version >= '3.6.2'", 41 | "version": "==3.6.1" 42 | }, 43 | "asgiref": { 44 | "hashes": [ 45 | "sha256:1d2880b792ae8757289136f1db2b7b99100ce959b2aa57fd69dab783d05afac4", 46 | "sha256:4a29362a6acebe09bf1d6640db38c1dc3d9217c68e6f9f6204d72667fc19a424" 47 | ], 48 | "markers": "python_version >= '3.7'", 49 | "version": "==3.5.2" 50 | }, 51 | "async-timeout": { 52 | "hashes": [ 53 | "sha256:2163e1640ddb52b7a8c80d0a67a08587e5d245cc9c553a74a847056bc2976b15", 54 | "sha256:8ca1e4fcf50d07413d66d1a5e416e42cfdf5851c981d679a09851a6853383b3c" 55 | ], 56 | "markers": "python_version >= '3.6'", 57 | "version": "==4.0.2" 58 | }, 59 | "asyncpg": { 60 | "hashes": [ 61 | "sha256:03f44926fa7ff7ccd59e98f05c7e227e9de15332a7da5bbcef3654bf468ee597", 62 | "sha256:050e339694f8c5d9aebcf326ca26f6622ef23963a6a3a4f97aeefc743954afd5", 63 | "sha256:0de408626cfc811ef04f372debfcdd5e4ab5aeb358f2ff14d1bdc246ed6272b5", 64 | "sha256:235205b60d4d014921f7b1cdca0e19669a9a8978f7606b3eb8237ca95f8e716e", 65 | "sha256:2ed3880b3aec8bda90548218fe0914d251d641f798382eda39a17abfc4910af0", 66 | "sha256:3ecbe8ed3af4c739addbfbd78f7752866cce2c4e9cc3f953556e4960349ae360", 67 | "sha256:49fc7220334cc31d14866a0b77a575d6a5945c0fa3bb67f17304e8b838e2a02b", 68 | "sha256:4b4051012ca75defa9a1dc6b78185ca58cdc3a247187eb76a6bcf55dfaa2fad4", 69 | "sha256:6d60f15a0ac18c54a6ca6507c28599c06e2e87a0901e7b548f15243d71905b18", 70 | "sha256:7129bd809990fd119e8b2b9982e80be7712bb6041cd082be3e415e60e5e2e98f", 71 | "sha256:77e684a24fee17ba3e487ca982d0259ed17bae1af68006f4cf284b23ba20ea2c", 72 | "sha256:838e4acd72da370ad07243898e886e93d3c0c9413f4444d600ba60a5cc206014", 73 | "sha256:868a71704262834065ca7113d80b1f679609e2df77d837747e3d92150dd5a39b", 74 | "sha256:8e1e79f0253cbd51fc43c4d0ce8804e46ee71f6c173fdc75606662ad18756b52", 75 | "sha256:9acb22a7b6bcca0d80982dce3d67f267d43e960544fb5dd934fd3abe20c48014", 76 | "sha256:a254d09a3a989cc1839ba2c34448b879cdd017b528a0cda142c92fbb6c13d957", 77 | "sha256:b0c3f39ebfac06848ba3f1e280cb1fada7cc1229538e3dad3146e8d1f9deb92a", 78 | "sha256:b1f7b173af649b85126429e11a628d01a5b75973d2a55d64dba19ad8f0e9f904", 79 | "sha256:d156e53b329e187e2dbfca8c28c999210045c45ef22a200b50de9b9e520c2694", 80 | "sha256:d96cf93e01df9fb03cef5f62346587805e6c0ca6f654c23b8d35315bdc69af59", 81 | "sha256:e550d8185f2c4725c1e8d3c555fe668b41bd092143012ddcc5343889e1c2a13d", 82 | "sha256:e5bd99ee7a00e87df97b804f178f31086e88c8106aca9703b1d7be5078999e68", 83 | "sha256:ede1a3a2c377fe12a3930f4b4dd5340e8b32929541d5db027a21816852723438", 84 | "sha256:efe056fd22fc6ed5c1ab353b6510808409566daac4e6f105e2043797f17b8dad", 85 | "sha256:f3ce7d8c0ab4639bbf872439eba86ef62dd030b245ad0e17c8c675d93d7a6b2d", 86 | "sha256:f92d501bf213b16fabad4fbb0061398d2bceae30ddc228e7314c28dcc6641b79" 87 | ], 88 | "index": "pypi", 89 | "version": "==0.26.0" 90 | }, 91 | "attrs": { 92 | "hashes": [ 93 | "sha256:29adc2665447e5191d0e7c568fde78b21f9672d344281d0c6e1ab085429b22b6", 94 | "sha256:86efa402f67bf2df34f51a335487cf46b1ec130d02b8d39fd248abfd30da551c" 95 | ], 96 | "markers": "python_version >= '3.5'", 97 | "version": "==22.1.0" 98 | }, 99 | "bcrypt": { 100 | "hashes": [ 101 | "sha256:0b0f0c7141622a31e9734b7f649451147c04ebb5122327ac0bd23744df84be90", 102 | "sha256:1c3334446fac200499e8bc04a530ce3cf0b3d7151e0e4ac5c0dddd3d95e97843", 103 | "sha256:2d0dd19aad87e4ab882ef1d12df505f4c52b28b69666ce83c528f42c07379227", 104 | "sha256:594780b364fb45f2634c46ec8d3e61c1c0f1811c4f2da60e8eb15594ecbf93ed", 105 | "sha256:7c7dd6c1f05bf89e65261d97ac3a6520f34c2acb369afb57e3ea4449be6ff8fd", 106 | "sha256:845b1daf4df2dd94d2fdbc9454953ca9dd0e12970a0bfc9f3dcc6faea3fa96e4", 107 | "sha256:8780e69f9deec9d60f947b169507d2c9816e4f11548f1f7ebee2af38b9b22ae4", 108 | "sha256:bf413f2a9b0a2950fc750998899013f2e718d20fa4a58b85ca50b6df5ed1bbf9", 109 | "sha256:bfb67f6a6c72dfb0a02f3df51550aa1862708e55128b22543e2b42c74f3620d7", 110 | "sha256:c59c170fc9225faad04dde1ba61d85b413946e8ce2e5f5f5ff30dfd67283f319", 111 | "sha256:dc6ec3dc19b1c193b2f7cf279d3e32e7caf447532fbcb7af0906fe4398900c33", 112 | "sha256:ede0f506554571c8eda80db22b83c139303ec6b595b8f60c4c8157bdd0bdee36" 113 | ], 114 | "version": "==4.0.0" 115 | }, 116 | "certifi": { 117 | "hashes": [ 118 | "sha256:43dadad18a7f168740e66944e4fa82c6611848ff9056ad910f8f7a3e46ab89e0", 119 | "sha256:cffdcd380919da6137f76633531a5817e3a9f268575c128249fb637e4f9e73fb" 120 | ], 121 | "markers": "python_version >= '3.6'", 122 | "version": "==2022.6.15.1" 123 | }, 124 | "cffi": { 125 | "hashes": [ 126 | "sha256:00a9ed42e88df81ffae7a8ab6d9356b371399b91dbdf0c3cb1e84c03a13aceb5", 127 | "sha256:03425bdae262c76aad70202debd780501fabeaca237cdfddc008987c0e0f59ef", 128 | "sha256:04ed324bda3cda42b9b695d51bb7d54b680b9719cfab04227cdd1e04e5de3104", 129 | "sha256:0e2642fe3142e4cc4af0799748233ad6da94c62a8bec3a6648bf8ee68b1c7426", 130 | "sha256:173379135477dc8cac4bc58f45db08ab45d228b3363adb7af79436135d028405", 131 | "sha256:198caafb44239b60e252492445da556afafc7d1e3ab7a1fb3f0584ef6d742375", 132 | "sha256:1e74c6b51a9ed6589199c787bf5f9875612ca4a8a0785fb2d4a84429badaf22a", 133 | "sha256:2012c72d854c2d03e45d06ae57f40d78e5770d252f195b93f581acf3ba44496e", 134 | "sha256:21157295583fe8943475029ed5abdcf71eb3911894724e360acff1d61c1d54bc", 135 | "sha256:2470043b93ff09bf8fb1d46d1cb756ce6132c54826661a32d4e4d132e1977adf", 136 | "sha256:285d29981935eb726a4399badae8f0ffdff4f5050eaa6d0cfc3f64b857b77185", 137 | "sha256:30d78fbc8ebf9c92c9b7823ee18eb92f2e6ef79b45ac84db507f52fbe3ec4497", 138 | "sha256:320dab6e7cb2eacdf0e658569d2575c4dad258c0fcc794f46215e1e39f90f2c3", 139 | "sha256:33ab79603146aace82c2427da5ca6e58f2b3f2fb5da893ceac0c42218a40be35", 140 | "sha256:3548db281cd7d2561c9ad9984681c95f7b0e38881201e157833a2342c30d5e8c", 141 | "sha256:3799aecf2e17cf585d977b780ce79ff0dc9b78d799fc694221ce814c2c19db83", 142 | "sha256:39d39875251ca8f612b6f33e6b1195af86d1b3e60086068be9cc053aa4376e21", 143 | "sha256:3b926aa83d1edb5aa5b427b4053dc420ec295a08e40911296b9eb1b6170f6cca", 144 | "sha256:3bcde07039e586f91b45c88f8583ea7cf7a0770df3a1649627bf598332cb6984", 145 | "sha256:3d08afd128ddaa624a48cf2b859afef385b720bb4b43df214f85616922e6a5ac", 146 | "sha256:3eb6971dcff08619f8d91607cfc726518b6fa2a9eba42856be181c6d0d9515fd", 147 | "sha256:40f4774f5a9d4f5e344f31a32b5096977b5d48560c5592e2f3d2c4374bd543ee", 148 | "sha256:4289fc34b2f5316fbb762d75362931e351941fa95fa18789191b33fc4cf9504a", 149 | "sha256:470c103ae716238bbe698d67ad020e1db9d9dba34fa5a899b5e21577e6d52ed2", 150 | "sha256:4f2c9f67e9821cad2e5f480bc8d83b8742896f1242dba247911072d4fa94c192", 151 | "sha256:50a74364d85fd319352182ef59c5c790484a336f6db772c1a9231f1c3ed0cbd7", 152 | "sha256:54a2db7b78338edd780e7ef7f9f6c442500fb0d41a5a4ea24fff1c929d5af585", 153 | "sha256:5635bd9cb9731e6d4a1132a498dd34f764034a8ce60cef4f5319c0541159392f", 154 | "sha256:59c0b02d0a6c384d453fece7566d1c7e6b7bae4fc5874ef2ef46d56776d61c9e", 155 | "sha256:5d598b938678ebf3c67377cdd45e09d431369c3b1a5b331058c338e201f12b27", 156 | "sha256:5df2768244d19ab7f60546d0c7c63ce1581f7af8b5de3eb3004b9b6fc8a9f84b", 157 | "sha256:5ef34d190326c3b1f822a5b7a45f6c4535e2f47ed06fec77d3d799c450b2651e", 158 | "sha256:6975a3fac6bc83c4a65c9f9fcab9e47019a11d3d2cf7f3c0d03431bf145a941e", 159 | "sha256:6c9a799e985904922a4d207a94eae35c78ebae90e128f0c4e521ce339396be9d", 160 | "sha256:70df4e3b545a17496c9b3f41f5115e69a4f2e77e94e1d2a8e1070bc0c38c8a3c", 161 | "sha256:7473e861101c9e72452f9bf8acb984947aa1661a7704553a9f6e4baa5ba64415", 162 | "sha256:8102eaf27e1e448db915d08afa8b41d6c7ca7a04b7d73af6514df10a3e74bd82", 163 | "sha256:87c450779d0914f2861b8526e035c5e6da0a3199d8f1add1a665e1cbc6fc6d02", 164 | "sha256:8b7ee99e510d7b66cdb6c593f21c043c248537a32e0bedf02e01e9553a172314", 165 | "sha256:91fc98adde3d7881af9b59ed0294046f3806221863722ba7d8d120c575314325", 166 | "sha256:94411f22c3985acaec6f83c6df553f2dbe17b698cc7f8ae751ff2237d96b9e3c", 167 | "sha256:98d85c6a2bef81588d9227dde12db8a7f47f639f4a17c9ae08e773aa9c697bf3", 168 | "sha256:9ad5db27f9cabae298d151c85cf2bad1d359a1b9c686a275df03385758e2f914", 169 | "sha256:a0b71b1b8fbf2b96e41c4d990244165e2c9be83d54962a9a1d118fd8657d2045", 170 | "sha256:a0f100c8912c114ff53e1202d0078b425bee3649ae34d7b070e9697f93c5d52d", 171 | "sha256:a591fe9e525846e4d154205572a029f653ada1a78b93697f3b5a8f1f2bc055b9", 172 | "sha256:a5c84c68147988265e60416b57fc83425a78058853509c1b0629c180094904a5", 173 | "sha256:a66d3508133af6e8548451b25058d5812812ec3798c886bf38ed24a98216fab2", 174 | "sha256:a8c4917bd7ad33e8eb21e9a5bbba979b49d9a97acb3a803092cbc1133e20343c", 175 | "sha256:b3bbeb01c2b273cca1e1e0c5df57f12dce9a4dd331b4fa1635b8bec26350bde3", 176 | "sha256:cba9d6b9a7d64d4bd46167096fc9d2f835e25d7e4c121fb2ddfc6528fb0413b2", 177 | "sha256:cc4d65aeeaa04136a12677d3dd0b1c0c94dc43abac5860ab33cceb42b801c1e8", 178 | "sha256:ce4bcc037df4fc5e3d184794f27bdaab018943698f4ca31630bc7f84a7b69c6d", 179 | "sha256:cec7d9412a9102bdc577382c3929b337320c4c4c4849f2c5cdd14d7368c5562d", 180 | "sha256:d400bfb9a37b1351253cb402671cea7e89bdecc294e8016a707f6d1d8ac934f9", 181 | "sha256:d61f4695e6c866a23a21acab0509af1cdfd2c013cf256bbf5b6b5e2695827162", 182 | "sha256:db0fbb9c62743ce59a9ff687eb5f4afbe77e5e8403d6697f7446e5f609976f76", 183 | "sha256:dd86c085fae2efd48ac91dd7ccffcfc0571387fe1193d33b6394db7ef31fe2a4", 184 | "sha256:e00b098126fd45523dd056d2efba6c5a63b71ffe9f2bbe1a4fe1716e1d0c331e", 185 | "sha256:e229a521186c75c8ad9490854fd8bbdd9a0c9aa3a524326b55be83b54d4e0ad9", 186 | "sha256:e263d77ee3dd201c3a142934a086a4450861778baaeeb45db4591ef65550b0a6", 187 | "sha256:ed9cb427ba5504c1dc15ede7d516b84757c3e3d7868ccc85121d9310d27eed0b", 188 | "sha256:fa6693661a4c91757f4412306191b6dc88c1703f780c8234035eac011922bc01", 189 | "sha256:fcd131dd944808b5bdb38e6f5b53013c5aa4f334c5cad0c72742f6eba4b73db0" 190 | ], 191 | "version": "==1.15.1" 192 | }, 193 | "charset-normalizer": { 194 | "hashes": [ 195 | "sha256:5a3d016c7c547f69d6f81fb0db9449ce888b418b5b9952cc5e6e66843e9dd845", 196 | "sha256:83e9a75d1911279afd89352c68b45348559d1fc0506b054b346651b5e7fee29f" 197 | ], 198 | "markers": "python_version >= '3.6'", 199 | "version": "==2.1.1" 200 | }, 201 | "click": { 202 | "hashes": [ 203 | "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e", 204 | "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48" 205 | ], 206 | "markers": "python_version >= '3.7'", 207 | "version": "==8.1.3" 208 | }, 209 | "cryptography": { 210 | "hashes": [ 211 | "sha256:0297ffc478bdd237f5ca3a7dc96fc0d315670bfa099c04dc3a4a2172008a405a", 212 | "sha256:10d1f29d6292fc95acb597bacefd5b9e812099d75a6469004fd38ba5471a977f", 213 | "sha256:16fa61e7481f4b77ef53991075de29fc5bacb582a1244046d2e8b4bb72ef66d0", 214 | "sha256:194044c6b89a2f9f169df475cc167f6157eb9151cc69af8a2a163481d45cc407", 215 | "sha256:1db3d807a14931fa317f96435695d9ec386be7b84b618cc61cfa5d08b0ae33d7", 216 | "sha256:3261725c0ef84e7592597606f6583385fed2a5ec3909f43bc475ade9729a41d6", 217 | "sha256:3b72c360427889b40f36dc214630e688c2fe03e16c162ef0aa41da7ab1455153", 218 | "sha256:3e3a2599e640927089f932295a9a247fc40a5bdf69b0484532f530471a382750", 219 | "sha256:3fc26e22840b77326a764ceb5f02ca2d342305fba08f002a8c1f139540cdfaad", 220 | "sha256:5067ee7f2bce36b11d0e334abcd1ccf8c541fc0bbdaf57cdd511fdee53e879b6", 221 | "sha256:52e7bee800ec869b4031093875279f1ff2ed12c1e2f74923e8f49c916afd1d3b", 222 | "sha256:64760ba5331e3f1794d0bcaabc0d0c39e8c60bf67d09c93dc0e54189dfd7cfe5", 223 | "sha256:765fa194a0f3372d83005ab83ab35d7c5526c4e22951e46059b8ac678b44fa5a", 224 | "sha256:79473cf8a5cbc471979bd9378c9f425384980fcf2ab6534b18ed7d0d9843987d", 225 | "sha256:896dd3a66959d3a5ddcfc140a53391f69ff1e8f25d93f0e2e7830c6de90ceb9d", 226 | "sha256:89ed49784ba88c221756ff4d4755dbc03b3c8d2c5103f6d6b4f83a0fb1e85294", 227 | "sha256:ac7e48f7e7261207d750fa7e55eac2d45f720027d5703cd9007e9b37bbb59ac0", 228 | "sha256:ad7353f6ddf285aeadfaf79e5a6829110106ff8189391704c1d8801aa0bae45a", 229 | "sha256:b0163a849b6f315bf52815e238bc2b2346604413fa7c1601eea84bcddb5fb9ac", 230 | "sha256:b6c9b706316d7b5a137c35e14f4103e2115b088c412140fdbd5f87c73284df61", 231 | "sha256:c2e5856248a416767322c8668ef1845ad46ee62629266f84a8f007a317141013", 232 | "sha256:ca9f6784ea96b55ff41708b92c3f6aeaebde4c560308e5fbbd3173fbc466e94e", 233 | "sha256:d1a5bd52d684e49a36582193e0b89ff267704cd4025abefb9e26803adeb3e5fb", 234 | "sha256:d3971e2749a723e9084dd507584e2a2761f78ad2c638aa31e80bc7a15c9db4f9", 235 | "sha256:d4ef6cc305394ed669d4d9eebf10d3a101059bdcf2669c366ec1d14e4fb227bd", 236 | "sha256:d9e69ae01f99abe6ad646947bba8941e896cb3aa805be2597a0400e0764b5818" 237 | ], 238 | "version": "==38.0.1" 239 | }, 240 | "databases": { 241 | "extras": [ 242 | "postgresql" 243 | ], 244 | "hashes": [ 245 | "sha256:0a69c6983a27e10a5b75ffa094486f1febadd9d5a8db016e69b8c2f6a354dc30", 246 | "sha256:47fae85d82d8227049f08b154019913c3ad2f6057ceb0b5ebb36703be6f5666b" 247 | ], 248 | "index": "pypi", 249 | "version": "==0.6.1" 250 | }, 251 | "dnspython": { 252 | "hashes": [ 253 | "sha256:0f7569a4a6ff151958b64304071d370daa3243d15941a7beedf0c9fe5105603e", 254 | "sha256:a851e51367fb93e9e1361732c1d60dab63eff98712e503ea7d92e6eccb109b4f" 255 | ], 256 | "markers": "python_version >= '3.6' and python_version < '4'", 257 | "version": "==2.2.1" 258 | }, 259 | "ecdsa": { 260 | "hashes": [ 261 | "sha256:190348041559e21b22a1d65cee485282ca11a6f81d503fddb84d5017e9ed1e49", 262 | "sha256:80600258e7ed2f16b9aa1d7c295bd70194109ad5a30fdee0eaeefef1d4c559dd" 263 | ], 264 | "markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'", 265 | "version": "==0.18.0" 266 | }, 267 | "email-validator": { 268 | "hashes": [ 269 | "sha256:6757aea012d40516357c0ac2b1a4c31219ab2f899d26831334c5d069e8b6c3d8", 270 | "sha256:c8589e691cf73eb99eed8d10ce0e9cbb05a0886ba920c8bcb7c82873f4c5789c" 271 | ], 272 | "version": "==1.2.1" 273 | }, 274 | "fastapi": { 275 | "extras": [ 276 | "all" 277 | ], 278 | "hashes": [ 279 | "sha256:694a2b6c2607a61029a4be1c6613f84d74019cb9f7a41c7a475dca8e715f9368", 280 | "sha256:96eb692350fe13d7a9843c3c87a874f0d45102975257dd224903efd6c0fde3bd" 281 | ], 282 | "index": "pypi", 283 | "version": "==0.83.0" 284 | }, 285 | "greenlet": { 286 | "hashes": [ 287 | "sha256:0118817c9341ef2b0f75f5af79ac377e4da6ff637e5ee4ac91802c0e379dadb4", 288 | "sha256:048d2bed76c2aa6de7af500ae0ea51dd2267aec0e0f2a436981159053d0bc7cc", 289 | "sha256:07c58e169bbe1e87b8bbf15a5c1b779a7616df9fd3e61cadc9d691740015b4f8", 290 | "sha256:095a980288fe05adf3d002fbb180c99bdcf0f930e220aa66fcd56e7914a38202", 291 | "sha256:0b181e9aa6cb2f5ec0cacc8cee6e5a3093416c841ba32c185c30c160487f0380", 292 | "sha256:1626185d938d7381631e48e6f7713e8d4b964be246073e1a1d15c2f061ac9f08", 293 | "sha256:184416e481295832350a4bf731ba619a92f5689bf5d0fa4341e98b98b1265bd7", 294 | "sha256:1dd51d2650e70c6c4af37f454737bf4a11e568945b27f74b471e8e2a9fd21268", 295 | "sha256:1ec2779774d8e42ed0440cf8bc55540175187e8e934f2be25199bf4ed948cd9e", 296 | "sha256:2cf45e339cabea16c07586306a31cfcc5a3b5e1626d365714d283732afed6809", 297 | "sha256:2fb0aa7f6996879551fd67461d5d3ab0c3c0245da98be90c89fcb7a18d437403", 298 | "sha256:44b4817c34c9272c65550b788913620f1fdc80362b209bc9d7dd2f40d8793080", 299 | "sha256:466ce0928e33421ee84ae04c4ac6f253a3a3e6b8d600a79bd43fd4403e0a7a76", 300 | "sha256:4f166b4aca8d7d489e82d74627a7069ab34211ef5ebb57c300ec4b9337b60fc0", 301 | "sha256:510c3b15587afce9800198b4b142202b323bf4b4b5f9d6c79cb9a35e5e3c30d2", 302 | "sha256:5b756e6730ea59b2745072e28ad27f4c837084688e6a6b3633c8b1e509e6ae0e", 303 | "sha256:5fbe1ab72b998ca77ceabbae63a9b2e2dc2d963f4299b9b278252ddba142d3f1", 304 | "sha256:6200a11f003ec26815f7e3d2ded01b43a3810be3528dd760d2f1fa777490c3cd", 305 | "sha256:65ad1a7a463a2a6f863661329a944a5802c7129f7ad33583dcc11069c17e622c", 306 | "sha256:694ffa7144fa5cc526c8f4512665003a39fa09ef00d19bbca5c8d3406db72fbe", 307 | "sha256:6f5d4b2280ceea76c55c893827961ed0a6eadd5a584a7c4e6e6dd7bc10dfdd96", 308 | "sha256:7532a46505470be30cbf1dbadb20379fb481244f1ca54207d7df3bf0bbab6a20", 309 | "sha256:76a53bfa10b367ee734b95988bd82a9a5f0038a25030f9f23bbbc005010ca600", 310 | "sha256:77e41db75f9958f2083e03e9dd39da12247b3430c92267df3af77c83d8ff9eed", 311 | "sha256:7a43bbfa9b6cfdfaeefbd91038dde65ea2c421dc387ed171613df340650874f2", 312 | "sha256:7b41d19c0cfe5c259fe6c539fd75051cd39a5d33d05482f885faf43f7f5e7d26", 313 | "sha256:7c5227963409551ae4a6938beb70d56bf1918c554a287d3da6853526212fbe0a", 314 | "sha256:870a48007872d12e95a996fca3c03a64290d3ea2e61076aa35d3b253cf34cd32", 315 | "sha256:88b04e12c9b041a1e0bcb886fec709c488192638a9a7a3677513ac6ba81d8e79", 316 | "sha256:8c287ae7ac921dfde88b1c125bd9590b7ec3c900c2d3db5197f1286e144e712b", 317 | "sha256:903fa5716b8fbb21019268b44f73f3748c41d1a30d71b4a49c84b642c2fed5fa", 318 | "sha256:9537e4baf0db67f382eb29255a03154fcd4984638303ff9baaa738b10371fa57", 319 | "sha256:9951dcbd37850da32b2cb6e391f621c1ee456191c6ae5528af4a34afe357c30e", 320 | "sha256:9b2f7d0408ddeb8ea1fd43d3db79a8cefaccadd2a812f021333b338ed6b10aba", 321 | "sha256:9c88e134d51d5e82315a7c32b914a58751b7353eb5268dbd02eabf020b4c4700", 322 | "sha256:9fae214f6c43cd47f7bef98c56919b9222481e833be2915f6857a1e9e8a15318", 323 | "sha256:a3a669f11289a8995d24fbfc0e63f8289dd03c9aaa0cc8f1eab31d18ca61a382", 324 | "sha256:aa741c1a8a8cc25eb3a3a01a62bdb5095a773d8c6a86470bde7f607a447e7905", 325 | "sha256:b0877a9a2129a2c56a2eae2da016743db7d9d6a05d5e1c198f1b7808c602a30e", 326 | "sha256:bcb6c6dd1d6be6d38d6db283747d07fda089ff8c559a835236560a4410340455", 327 | "sha256:caff52cb5cd7626872d9696aee5b794abe172804beb7db52eed1fd5824b63910", 328 | "sha256:cbc1eb55342cbac8f7ec159088d54e2cfdd5ddf61c87b8bbe682d113789331b2", 329 | "sha256:cd16a89efe3a003029c87ff19e9fba635864e064da646bc749fc1908a4af18f3", 330 | "sha256:ce5b64dfe8d0cca407d88b0ee619d80d4215a2612c1af8c98a92180e7109f4b5", 331 | "sha256:d58a5a71c4c37354f9e0c24c9c8321f0185f6945ef027460b809f4bb474bfe41", 332 | "sha256:db41f3845eb579b544c962864cce2c2a0257fe30f0f1e18e51b1e8cbb4e0ac6d", 333 | "sha256:db5b25265010a1b3dca6a174a443a0ed4c4ab12d5e2883a11c97d6e6d59b12f9", 334 | "sha256:dd0404d154084a371e6d2bafc787201612a1359c2dee688ae334f9118aa0bf47", 335 | "sha256:de431765bd5fe62119e0bc6bc6e7b17ac53017ae1782acf88fcf6b7eae475a49", 336 | "sha256:df02fdec0c533301497acb0bc0f27f479a3a63dcdc3a099ae33a902857f07477", 337 | "sha256:e8533f5111704d75de3139bf0b8136d3a6c1642c55c067866fa0a51c2155ee33", 338 | "sha256:f2f908239b7098799b8845e5936c2ccb91d8c2323be02e82f8dcb4a80dcf4a25", 339 | "sha256:f8bfd36f368efe0ab2a6aa3db7f14598aac454b06849fb633b762ddbede1db90", 340 | "sha256:ffe73f9e7aea404722058405ff24041e59d31ca23d1da0895af48050a07b6932" 341 | ], 342 | "markers": "python_version >= '3' and platform_machine == 'aarch64' or (platform_machine == 'ppc64le' or (platform_machine == 'x86_64' or (platform_machine == 'amd64' or (platform_machine == 'AMD64' or (platform_machine == 'win32' or platform_machine == 'WIN32')))))", 343 | "version": "==1.1.3" 344 | }, 345 | "h11": { 346 | "hashes": [ 347 | "sha256:70813c1135087a248a4d38cc0e1a0181ffab2188141a93eaf567940c3957ff06", 348 | "sha256:8ddd78563b633ca55346c8cd41ec0af27d3c79931828beffb46ce70a379e7442" 349 | ], 350 | "markers": "python_version >= '3.6'", 351 | "version": "==0.13.0" 352 | }, 353 | "httptools": { 354 | "hashes": [ 355 | "sha256:0297822cea9f90a38df29f48e40b42ac3d48a28637368f3ec6d15eebefd182f9", 356 | "sha256:1af91b3650ce518d226466f30bbba5b6376dbd3ddb1b2be8b0658c6799dd450b", 357 | "sha256:1f90cd6fd97c9a1b7fe9215e60c3bd97336742a0857f00a4cb31547bc22560c2", 358 | "sha256:24bb4bb8ac3882f90aa95403a1cb48465de877e2d5298ad6ddcfdebec060787d", 359 | "sha256:295874861c173f9101960bba332429bb77ed4dcd8cdf5cee9922eb00e4f6bc09", 360 | "sha256:3625a55886257755cb15194efbf209584754e31d336e09e2ffe0685a76cb4b60", 361 | "sha256:3a47a34f6015dd52c9eb629c0f5a8a5193e47bf2a12d9a3194d231eaf1bc451a", 362 | "sha256:3cb8acf8f951363b617a8420768a9f249099b92e703c052f9a51b66342eea89b", 363 | "sha256:4b098e4bb1174096a93f48f6193e7d9aa7071506a5877da09a783509ca5fff42", 364 | "sha256:4d9ebac23d2de960726ce45f49d70eb5466725c0087a078866043dad115f850f", 365 | "sha256:50d4613025f15f4b11f1c54bbed4761c0020f7f921b95143ad6d58c151198142", 366 | "sha256:5230a99e724a1bdbbf236a1b58d6e8504b912b0552721c7c6b8570925ee0ccde", 367 | "sha256:54465401dbbec9a6a42cf737627fb0f014d50dc7365a6b6cd57753f151a86ff0", 368 | "sha256:550059885dc9c19a072ca6d6735739d879be3b5959ec218ba3e013fd2255a11b", 369 | "sha256:557be7fbf2bfa4a2ec65192c254e151684545ebab45eca5d50477d562c40f986", 370 | "sha256:5b65be160adcd9de7a7e6413a4966665756e263f0d5ddeffde277ffeee0576a5", 371 | "sha256:64eba6f168803a7469866a9c9b5263a7463fa8b7a25b35e547492aa7322036b6", 372 | "sha256:72ad589ba5e4a87e1d404cc1cb1b5780bfcb16e2aec957b88ce15fe879cc08ca", 373 | "sha256:7d0c1044bce274ec6711f0770fd2d5544fe392591d204c68328e60a46f88843b", 374 | "sha256:7e5eefc58d20e4c2da82c78d91b2906f1a947ef42bd668db05f4ab4201a99f49", 375 | "sha256:850fec36c48df5a790aa735417dca8ce7d4b48d59b3ebd6f83e88a8125cde324", 376 | "sha256:85b392aba273566c3d5596a0a490978c085b79700814fb22bfd537d381dd230c", 377 | "sha256:8c2a56b6aad7cc8f5551d8e04ff5a319d203f9d870398b94702300de50190f63", 378 | "sha256:8f470c79061599a126d74385623ff4744c4e0f4a0997a353a44923c0b561ee51", 379 | "sha256:8ffce9d81c825ac1deaa13bc9694c0562e2840a48ba21cfc9f3b4c922c16f372", 380 | "sha256:9423a2de923820c7e82e18980b937893f4aa8251c43684fa1772e341f6e06887", 381 | "sha256:9b571b281a19762adb3f48a7731f6842f920fa71108aff9be49888320ac3e24d", 382 | "sha256:a04fe458a4597aa559b79c7f48fe3dceabef0f69f562daf5c5e926b153817281", 383 | "sha256:aa47ffcf70ba6f7848349b8a6f9b481ee0f7637931d91a9860a1838bfc586901", 384 | "sha256:bede7ee075e54b9a5bde695b4fc8f569f30185891796b2e4e09e2226801d09bd", 385 | "sha256:c1d2357f791b12d86faced7b5736dea9ef4f5ecdc6c3f253e445ee82da579449", 386 | "sha256:c6eeefd4435055a8ebb6c5cc36111b8591c192c56a95b45fe2af22d9881eee25", 387 | "sha256:ca1b7becf7d9d3ccdbb2f038f665c0f4857e08e1d8481cbcc1a86a0afcfb62b2", 388 | "sha256:e67d4f8734f8054d2c4858570cc4b233bf753f56e85217de4dfb2495904cf02e", 389 | "sha256:e8a34e4c0ab7b1ca17b8763613783e2458e77938092c18ac919420ab8655c8c1", 390 | "sha256:e90491a4d77d0cb82e0e7a9cb35d86284c677402e4ce7ba6b448ccc7325c5421", 391 | "sha256:ef1616b3ba965cd68e6f759eeb5d34fbf596a79e84215eeceebf34ba3f61fdc7", 392 | "sha256:f222e1e9d3f13b68ff8a835574eda02e67277d51631d69d7cf7f8e07df678c86", 393 | "sha256:f5e3088f4ed33947e16fd865b8200f9cfae1144f41b64a8cf19b599508e096bc", 394 | "sha256:f659d7a48401158c59933904040085c200b4be631cb5f23a7d561fbae593ec1f", 395 | "sha256:fe9c766a0c35b7e3d6b6939393c8dfdd5da3ac5dec7f971ec9134f284c6c36d6" 396 | ], 397 | "version": "==0.5.0" 398 | }, 399 | "idna": { 400 | "hashes": [ 401 | "sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff", 402 | "sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d" 403 | ], 404 | "markers": "python_version >= '3.5'", 405 | "version": "==3.3" 406 | }, 407 | "iniconfig": { 408 | "hashes": [ 409 | "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3", 410 | "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32" 411 | ], 412 | "version": "==1.1.1" 413 | }, 414 | "itsdangerous": { 415 | "hashes": [ 416 | "sha256:2c2349112351b88699d8d4b6b075022c0808887cb7ad10069318a8b0bc88db44", 417 | "sha256:5dbbc68b317e5e42f327f9021763545dc3fc3bfe22e6deb96aaf1fc38874156a" 418 | ], 419 | "version": "==2.1.2" 420 | }, 421 | "jinja2": { 422 | "hashes": [ 423 | "sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852", 424 | "sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61" 425 | ], 426 | "version": "==3.1.2" 427 | }, 428 | "mako": { 429 | "hashes": [ 430 | "sha256:3724869b363ba630a272a5f89f68c070352137b8fd1757650017b7e06fda163f", 431 | "sha256:8efcb8004681b5f71d09c983ad5a9e6f5c40601a6ec469148753292abc0da534" 432 | ], 433 | "markers": "python_version >= '3.7'", 434 | "version": "==1.2.2" 435 | }, 436 | "markupsafe": { 437 | "hashes": [ 438 | "sha256:0212a68688482dc52b2d45013df70d169f542b7394fc744c02a57374a4207003", 439 | "sha256:089cf3dbf0cd6c100f02945abeb18484bd1ee57a079aefd52cffd17fba910b88", 440 | "sha256:10c1bfff05d95783da83491be968e8fe789263689c02724e0c691933c52994f5", 441 | "sha256:33b74d289bd2f5e527beadcaa3f401e0df0a89927c1559c8566c066fa4248ab7", 442 | "sha256:3799351e2336dc91ea70b034983ee71cf2f9533cdff7c14c90ea126bfd95d65a", 443 | "sha256:3ce11ee3f23f79dbd06fb3d63e2f6af7b12db1d46932fe7bd8afa259a5996603", 444 | "sha256:421be9fbf0ffe9ffd7a378aafebbf6f4602d564d34be190fc19a193232fd12b1", 445 | "sha256:43093fb83d8343aac0b1baa75516da6092f58f41200907ef92448ecab8825135", 446 | "sha256:46d00d6cfecdde84d40e572d63735ef81423ad31184100411e6e3388d405e247", 447 | "sha256:4a33dea2b688b3190ee12bd7cfa29d39c9ed176bda40bfa11099a3ce5d3a7ac6", 448 | "sha256:4b9fe39a2ccc108a4accc2676e77da025ce383c108593d65cc909add5c3bd601", 449 | "sha256:56442863ed2b06d19c37f94d999035e15ee982988920e12a5b4ba29b62ad1f77", 450 | "sha256:671cd1187ed5e62818414afe79ed29da836dde67166a9fac6d435873c44fdd02", 451 | "sha256:694deca8d702d5db21ec83983ce0bb4b26a578e71fbdbd4fdcd387daa90e4d5e", 452 | "sha256:6a074d34ee7a5ce3effbc526b7083ec9731bb3cbf921bbe1d3005d4d2bdb3a63", 453 | "sha256:6d0072fea50feec76a4c418096652f2c3238eaa014b2f94aeb1d56a66b41403f", 454 | "sha256:6fbf47b5d3728c6aea2abb0589b5d30459e369baa772e0f37a0320185e87c980", 455 | "sha256:7f91197cc9e48f989d12e4e6fbc46495c446636dfc81b9ccf50bb0ec74b91d4b", 456 | "sha256:86b1f75c4e7c2ac2ccdaec2b9022845dbb81880ca318bb7a0a01fbf7813e3812", 457 | "sha256:8dc1c72a69aa7e082593c4a203dcf94ddb74bb5c8a731e4e1eb68d031e8498ff", 458 | "sha256:8e3dcf21f367459434c18e71b2a9532d96547aef8a871872a5bd69a715c15f96", 459 | "sha256:8e576a51ad59e4bfaac456023a78f6b5e6e7651dcd383bcc3e18d06f9b55d6d1", 460 | "sha256:96e37a3dc86e80bf81758c152fe66dbf60ed5eca3d26305edf01892257049925", 461 | "sha256:97a68e6ada378df82bc9f16b800ab77cbf4b2fada0081794318520138c088e4a", 462 | "sha256:99a2a507ed3ac881b975a2976d59f38c19386d128e7a9a18b7df6fff1fd4c1d6", 463 | "sha256:a49907dd8420c5685cfa064a1335b6754b74541bbb3706c259c02ed65b644b3e", 464 | "sha256:b09bf97215625a311f669476f44b8b318b075847b49316d3e28c08e41a7a573f", 465 | "sha256:b7bd98b796e2b6553da7225aeb61f447f80a1ca64f41d83612e6139ca5213aa4", 466 | "sha256:b87db4360013327109564f0e591bd2a3b318547bcef31b468a92ee504d07ae4f", 467 | "sha256:bcb3ed405ed3222f9904899563d6fc492ff75cce56cba05e32eff40e6acbeaa3", 468 | "sha256:d4306c36ca495956b6d568d276ac11fdd9c30a36f1b6eb928070dc5360b22e1c", 469 | "sha256:d5ee4f386140395a2c818d149221149c54849dfcfcb9f1debfe07a8b8bd63f9a", 470 | "sha256:dda30ba7e87fbbb7eab1ec9f58678558fd9a6b8b853530e176eabd064da81417", 471 | "sha256:e04e26803c9c3851c931eac40c695602c6295b8d432cbe78609649ad9bd2da8a", 472 | "sha256:e1c0b87e09fa55a220f058d1d49d3fb8df88fbfab58558f1198e08c1e1de842a", 473 | "sha256:e72591e9ecd94d7feb70c1cbd7be7b3ebea3f548870aa91e2732960fa4d57a37", 474 | "sha256:e8c843bbcda3a2f1e3c2ab25913c80a3c5376cd00c6e8c4a86a89a28c8dc5452", 475 | "sha256:efc1913fd2ca4f334418481c7e595c00aad186563bbc1ec76067848c7ca0a933", 476 | "sha256:f121a1420d4e173a5d96e47e9a0c0dcff965afdf1626d28de1460815f7c4ee7a", 477 | "sha256:fc7b548b17d238737688817ab67deebb30e8073c95749d55538ed473130ec0c7" 478 | ], 479 | "markers": "python_version >= '3.7'", 480 | "version": "==2.1.1" 481 | }, 482 | "orjson": { 483 | "hashes": [ 484 | "sha256:02d638d43951ba346a80f0abd5942a872cc87db443e073f6f6fc530fee81e19b", 485 | "sha256:03ed95814140ff09f550b3a42e6821f855d981c94d25b9cc83e8cca431525d70", 486 | "sha256:1b1cd25acfa77935bb2e791b75211cec0cfc21227fe29387e553c545c3ff87e1", 487 | "sha256:2058653cc12b90e482beacb5c2d52dc3d7606f9e9f5a52c1c10ef49371e76f52", 488 | "sha256:2065b6d280dc58f131ffd93393737961ff68ae7eb6884b68879394074cc03c13", 489 | "sha256:25b5e48fbb9f0b428a5e44cf740675c9281dd67816149fc33659803399adbbe8", 490 | "sha256:2bdb1042970ca5f544a047d6c235a7eb4acdb69df75441dd1dfcbc406377ab37", 491 | "sha256:2d81e6e56bbea44be0222fb53f7b255b4e7426290516771592738ca01dbd053b", 492 | "sha256:3c7225e8b08996d1a0c804d3a641a53e796685e8c9a9fd52bd428980032cad9a", 493 | "sha256:3e2459d441ab8fd8b161aa305a73d5269b3cda13b5a2a39eba58b4dd3e394f49", 494 | "sha256:4065906ce3ad6195ac4d1bddde862fe811a42d7be237a1ff762666c3a4bb2151", 495 | "sha256:5b072ef8520cfe7bd4db4e3c9972d94336763c2253f7c4718a49e8733bada7b8", 496 | "sha256:5edb93cdd3eb32977633fa7aaa6a34b8ab54d9c49cdcc6b0d42c247a29091b22", 497 | "sha256:5f856279872a4449fc629924e6a083b9821e366cf98b14c63c308269336f7c14", 498 | "sha256:5fd6cac83136e06e538a4d17117eaeabec848c1e86f5742d4811656ad7ee475f", 499 | "sha256:6433c956f4a18112342a18281e0bec67fcd8b90be3a5271556c09226e045d805", 500 | "sha256:655d7387a1634a9a477c545eea92a1ee902ab28626d701c6de4914e2ed0fecd2", 501 | "sha256:66c19399bb3b058e3236af7910b57b19a4fc221459d722ed72a7dc90370ca090", 502 | "sha256:6a23b40c98889e9abac084ce5a1fb251664b41da9f6bdb40a4729e2288ed2ed4", 503 | "sha256:6e3da2e4bd27c3b796519ca74132c7b9e5348fb6746315e0f6c1592bc5cf1caf", 504 | "sha256:6ea5fe20ef97545e14dd4d0263e4c5c3bc3d2248d39b4b0aed4b84d528dfc0af", 505 | "sha256:7536a2a0b41672f824912aeab545c2467a9ff5ca73a066ff04fb81043a0a177a", 506 | "sha256:7990a9caf3b34016ac30be5e6cfc4e7efd76aa85614a1215b0eae4f0c7e3db59", 507 | "sha256:7b0e72974a5d3b101226899f111368ec2c9824d3e9804af0e5b31567f53ad98a", 508 | "sha256:87462791dd57de2e3e53068bf4b7169c125c50960f1bdda08ed30c797cb42a56", 509 | "sha256:896a21a07f1998648d9998e881ab2b6b80d5daac4c31188535e9d50460edfcf7", 510 | "sha256:8b391d5c2ddc2f302d22909676b306cb6521022c3ee306c861a6935670291b2c", 511 | "sha256:8f687776a03c19f40b982fb5c414221b7f3d19097841571be2223d1569a59877", 512 | "sha256:9a93850a1bdc300177b111b4b35b35299f046148ba23020f91d6efd7bf6b9d20", 513 | "sha256:9e6ac22cec72d5b39035b566e4b86c74b84866f12b5b0b6541506a080fb67d6d", 514 | "sha256:a709c2249c1f2955dbf879506fd43fa08c31fdb79add9aeb891e3338b648bf60", 515 | "sha256:b68a42a31f8429728183c21fb440c21de1b62e5378d0d73f280e2d894ef8942e", 516 | "sha256:be02f6acee33bb63862eeff80548cd6b8a62e2d60ad2d8dfd5a8824cc43d8887", 517 | "sha256:d189e2acb510e374700cb98cf11b54f0179916ee40f8453b836157ae293efa79", 518 | "sha256:d2b5dafbe68237a792143137cba413447f60dd5df428e05d73dcba10c1ea6fcf", 519 | "sha256:e1418feeb8b698b9224b1f024555895169d481604d5d884498c1838d7412794c", 520 | "sha256:e2defd9527651ad39ec20ae03c812adf47ef7662bdd6bc07dabb10888d70dc62", 521 | "sha256:e2f4a5542f50e3d336a18cb224fc757245ca66b1fd0b70b5dd4471b8ff5f2b0e", 522 | "sha256:e68c699471ea3e2dd1b35bfd71c6a0a0e4885b64abbe2d98fce1ef11e0afaff3", 523 | "sha256:f4b46dbdda2f0bd6480c39db90b21340a19c3b0fcf34bc4c6e465332930ca539", 524 | "sha256:fb42f7cf57d5804a9daa6b624e3490ec9e2631e042415f3aebe9f35a8492ba6c", 525 | "sha256:ff13410ddbdda5d4197a4a4c09969cb78c722a67550f0a63c02c07aadc624833" 526 | ], 527 | "version": "==3.8.0" 528 | }, 529 | "packaging": { 530 | "hashes": [ 531 | "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb", 532 | "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522" 533 | ], 534 | "markers": "python_version >= '3.6'", 535 | "version": "==21.3" 536 | }, 537 | "passlib": { 538 | "extras": [ 539 | "bcrypt" 540 | ], 541 | "hashes": [ 542 | "sha256:aa6bca462b8d8bda89c70b382f0c298a20b5560af6cbfa2dce410c0a2fb669f1", 543 | "sha256:defd50f72b65c5402ab2c573830a6978e5f202ad0d984793c8dde2c4152ebe04" 544 | ], 545 | "index": "pypi", 546 | "version": "==1.7.4" 547 | }, 548 | "pluggy": { 549 | "hashes": [ 550 | "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159", 551 | "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3" 552 | ], 553 | "markers": "python_version >= '3.6'", 554 | "version": "==1.0.0" 555 | }, 556 | "psycopg2": { 557 | "hashes": [ 558 | "sha256:06f32425949bd5fe8f625c49f17ebb9784e1e4fe928b7cce72edc36fb68e4c0c", 559 | "sha256:0762c27d018edbcb2d34d51596e4346c983bd27c330218c56c4dc25ef7e819bf", 560 | "sha256:083707a696e5e1c330af2508d8fab36f9700b26621ccbcb538abe22e15485362", 561 | "sha256:34b33e0162cfcaad151f249c2649fd1030010c16f4bbc40a604c1cb77173dcf7", 562 | "sha256:4295093a6ae3434d33ec6baab4ca5512a5082cc43c0505293087b8a46d108461", 563 | "sha256:8cf3878353cc04b053822896bc4922b194792df9df2f1ad8da01fb3043602126", 564 | "sha256:8e841d1bf3434da985cc5ef13e6f75c8981ced601fd70cc6bf33351b91562981", 565 | "sha256:9572e08b50aed176ef6d66f15a21d823bb6f6d23152d35e8451d7d2d18fdac56", 566 | "sha256:a81e3866f99382dfe8c15a151f1ca5fde5815fde879348fe5a9884a7c092a305", 567 | "sha256:cb10d44e6694d763fa1078a26f7f6137d69f555a78ec85dc2ef716c37447e4b2", 568 | "sha256:d3ca6421b942f60c008f81a3541e8faf6865a28d5a9b48544b0ee4f40cac7fca" 569 | ], 570 | "index": "pypi", 571 | "version": "==2.9.3" 572 | }, 573 | "py": { 574 | "hashes": [ 575 | "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719", 576 | "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378" 577 | ], 578 | "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", 579 | "version": "==1.11.0" 580 | }, 581 | "pyasn1": { 582 | "hashes": [ 583 | "sha256:014c0e9976956a08139dc0712ae195324a75e142284d5f87f1a87ee1b068a359", 584 | "sha256:03840c999ba71680a131cfaee6fab142e1ed9bbd9c693e285cc6aca0d555e576", 585 | "sha256:0458773cfe65b153891ac249bcf1b5f8f320b7c2ce462151f8fa74de8934becf", 586 | "sha256:08c3c53b75eaa48d71cf8c710312316392ed40899cb34710d092e96745a358b7", 587 | "sha256:39c7e2ec30515947ff4e87fb6f456dfc6e84857d34be479c9d4a4ba4bf46aa5d", 588 | "sha256:5c9414dcfede6e441f7e8f81b43b34e834731003427e5b09e4e00e3172a10f00", 589 | "sha256:6e7545f1a61025a4e58bb336952c5061697da694db1cae97b116e9c46abcf7c8", 590 | "sha256:78fa6da68ed2727915c4767bb386ab32cdba863caa7dbe473eaae45f9959da86", 591 | "sha256:7ab8a544af125fb704feadb008c99a88805126fb525280b2270bb25cc1d78a12", 592 | "sha256:99fcc3c8d804d1bc6d9a099921e39d827026409a58f2a720dcdb89374ea0c776", 593 | "sha256:aef77c9fb94a3ac588e87841208bdec464471d9871bd5050a287cc9a475cd0ba", 594 | "sha256:e89bf84b5437b532b0803ba5c9a5e054d21fec423a89952a74f87fa2c9b7bce2", 595 | "sha256:fec3e9d8e36808a28efb59b489e4528c10ad0f480e57dcc32b4de5c9d8c9fdf3" 596 | ], 597 | "version": "==0.4.8" 598 | }, 599 | "pycparser": { 600 | "hashes": [ 601 | "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9", 602 | "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206" 603 | ], 604 | "version": "==2.21" 605 | }, 606 | "pydantic": { 607 | "hashes": [ 608 | "sha256:05e00dbebbe810b33c7a7362f231893183bcc4251f3f2ff991c31d5c08240c42", 609 | "sha256:06094d18dd5e6f2bbf93efa54991c3240964bb663b87729ac340eb5014310624", 610 | "sha256:0b959f4d8211fc964772b595ebb25f7652da3f22322c007b6fed26846a40685e", 611 | "sha256:19b3b9ccf97af2b7519c42032441a891a5e05c68368f40865a90eb88833c2559", 612 | "sha256:1b6ee725bd6e83ec78b1aa32c5b1fa67a3a65badddde3976bca5fe4568f27709", 613 | "sha256:1ee433e274268a4b0c8fde7ad9d58ecba12b069a033ecc4645bb6303c062d2e9", 614 | "sha256:216f3bcbf19c726b1cc22b099dd409aa371f55c08800bcea4c44c8f74b73478d", 615 | "sha256:2d0567e60eb01bccda3a4df01df677adf6b437958d35c12a3ac3e0f078b0ee52", 616 | "sha256:2e05aed07fa02231dbf03d0adb1be1d79cabb09025dd45aa094aa8b4e7b9dcda", 617 | "sha256:352aedb1d71b8b0736c6d56ad2bd34c6982720644b0624462059ab29bd6e5912", 618 | "sha256:355639d9afc76bcb9b0c3000ddcd08472ae75318a6eb67a15866b87e2efa168c", 619 | "sha256:37c90345ec7dd2f1bcef82ce49b6235b40f282b94d3eec47e801baf864d15525", 620 | "sha256:4b8795290deaae348c4eba0cebb196e1c6b98bdbe7f50b2d0d9a4a99716342fe", 621 | "sha256:5760e164b807a48a8f25f8aa1a6d857e6ce62e7ec83ea5d5c5a802eac81bad41", 622 | "sha256:6eb843dcc411b6a2237a694f5e1d649fc66c6064d02b204a7e9d194dff81eb4b", 623 | "sha256:7b5ba54d026c2bd2cb769d3468885f23f43710f651688e91f5fb1edcf0ee9283", 624 | "sha256:7c2abc4393dea97a4ccbb4ec7d8658d4e22c4765b7b9b9445588f16c71ad9965", 625 | "sha256:81a7b66c3f499108b448f3f004801fcd7d7165fb4200acb03f1c2402da73ce4c", 626 | "sha256:91b8e218852ef6007c2b98cd861601c6a09f1aa32bbbb74fab5b1c33d4a1e410", 627 | "sha256:9300fcbebf85f6339a02c6994b2eb3ff1b9c8c14f502058b5bf349d42447dcf5", 628 | "sha256:9cabf4a7f05a776e7793e72793cd92cc865ea0e83a819f9ae4ecccb1b8aa6116", 629 | "sha256:a1f5a63a6dfe19d719b1b6e6106561869d2efaca6167f84f5ab9347887d78b98", 630 | "sha256:a4c805731c33a8db4b6ace45ce440c4ef5336e712508b4d9e1aafa617dc9907f", 631 | "sha256:ae544c47bec47a86bc7d350f965d8b15540e27e5aa4f55170ac6a75e5f73b644", 632 | "sha256:b97890e56a694486f772d36efd2ba31612739bc6f3caeee50e9e7e3ebd2fdd13", 633 | "sha256:bb6ad4489af1bac6955d38ebcb95079a836af31e4c4f74aba1ca05bb9f6027bd", 634 | "sha256:bedf309630209e78582ffacda64a21f96f3ed2e51fbf3962d4d488e503420254", 635 | "sha256:c1ba1afb396148bbc70e9eaa8c06c1716fdddabaf86e7027c5988bae2a829ab6", 636 | "sha256:c33602f93bfb67779f9c507e4d69451664524389546bacfe1bee13cae6dc7488", 637 | "sha256:c4aac8e7103bf598373208f6299fa9a5cfd1fc571f2d40bf1dd1955a63d6eeb5", 638 | "sha256:c6f981882aea41e021f72779ce2a4e87267458cc4d39ea990729e21ef18f0f8c", 639 | "sha256:cc78cc83110d2f275ec1970e7a831f4e371ee92405332ebfe9860a715f8336e1", 640 | "sha256:d49f3db871575e0426b12e2f32fdb25e579dea16486a26e5a0474af87cb1ab0a", 641 | "sha256:dd3f9a40c16daf323cf913593083698caee97df2804aa36c4b3175d5ac1b92a2", 642 | "sha256:e0bedafe4bc165ad0a56ac0bd7695df25c50f76961da29c050712596cf092d6d", 643 | "sha256:e9069e1b01525a96e6ff49e25876d90d5a563bc31c658289a8772ae186552236" 644 | ], 645 | "markers": "python_version >= '3.7'", 646 | "version": "==1.10.2" 647 | }, 648 | "pyparsing": { 649 | "hashes": [ 650 | "sha256:2b020ecf7d21b687f219b71ecad3631f644a47f01403fa1d1036b0c6416d70fb", 651 | "sha256:5026bae9a10eeaefb61dab2f09052b9f4307d44aee4eda64b309723d8d206bbc" 652 | ], 653 | "markers": "python_full_version >= '3.6.8'", 654 | "version": "==3.0.9" 655 | }, 656 | "pytest": { 657 | "hashes": [ 658 | "sha256:1377bda3466d70b55e3f5cecfa55bb7cfcf219c7964629b967c37cf0bda818b7", 659 | "sha256:4f365fec2dff9c1162f834d9f18af1ba13062db0c708bf7b946f8a5c76180c39" 660 | ], 661 | "index": "pypi", 662 | "version": "==7.1.3" 663 | }, 664 | "python-dotenv": { 665 | "hashes": [ 666 | "sha256:1684eb44636dd462b66c3ee016599815514527ad99965de77f43e0944634a7e5", 667 | "sha256:b77d08274639e3d34145dfa6c7008e66df0f04b7be7a75fd0d5292c191d79045" 668 | ], 669 | "version": "==0.21.0" 670 | }, 671 | "python-jose": { 672 | "extras": [ 673 | "cryptography" 674 | ], 675 | "hashes": [ 676 | "sha256:55779b5e6ad599c6336191246e95eb2293a9ddebd555f796a65f838f07e5d78a", 677 | "sha256:9b1376b023f8b298536eedd47ae1089bcdb848f1535ab30555cd92002d78923a" 678 | ], 679 | "index": "pypi", 680 | "version": "==3.3.0" 681 | }, 682 | "python-multipart": { 683 | "hashes": [ 684 | "sha256:f7bb5f611fc600d15fa47b3974c8aa16e93724513b49b5f95c81e6624c83fa43" 685 | ], 686 | "index": "pypi", 687 | "version": "==0.0.5" 688 | }, 689 | "pyyaml": { 690 | "hashes": [ 691 | "sha256:0283c35a6a9fbf047493e3a0ce8d79ef5030852c51e9d911a27badfde0605293", 692 | "sha256:055d937d65826939cb044fc8c9b08889e8c743fdc6a32b33e2390f66013e449b", 693 | "sha256:07751360502caac1c067a8132d150cf3d61339af5691fe9e87803040dbc5db57", 694 | "sha256:0b4624f379dab24d3725ffde76559cff63d9ec94e1736b556dacdfebe5ab6d4b", 695 | "sha256:0ce82d761c532fe4ec3f87fc45688bdd3a4c1dc5e0b4a19814b9009a29baefd4", 696 | "sha256:1e4747bc279b4f613a09eb64bba2ba602d8a6664c6ce6396a4d0cd413a50ce07", 697 | "sha256:213c60cd50106436cc818accf5baa1aba61c0189ff610f64f4a3e8c6726218ba", 698 | "sha256:231710d57adfd809ef5d34183b8ed1eeae3f76459c18fb4a0b373ad56bedcdd9", 699 | "sha256:277a0ef2981ca40581a47093e9e2d13b3f1fbbeffae064c1d21bfceba2030287", 700 | "sha256:2cd5df3de48857ed0544b34e2d40e9fac445930039f3cfe4bcc592a1f836d513", 701 | "sha256:40527857252b61eacd1d9af500c3337ba8deb8fc298940291486c465c8b46ec0", 702 | "sha256:473f9edb243cb1935ab5a084eb238d842fb8f404ed2193a915d1784b5a6b5fc0", 703 | "sha256:48c346915c114f5fdb3ead70312bd042a953a8ce5c7106d5bfb1a5254e47da92", 704 | "sha256:50602afada6d6cbfad699b0c7bb50d5ccffa7e46a3d738092afddc1f9758427f", 705 | "sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2", 706 | "sha256:77f396e6ef4c73fdc33a9157446466f1cff553d979bd00ecb64385760c6babdc", 707 | "sha256:819b3830a1543db06c4d4b865e70ded25be52a2e0631ccd2f6a47a2822f2fd7c", 708 | "sha256:897b80890765f037df3403d22bab41627ca8811ae55e9a722fd0392850ec4d86", 709 | "sha256:98c4d36e99714e55cfbaaee6dd5badbc9a1ec339ebfc3b1f52e293aee6bb71a4", 710 | "sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c", 711 | "sha256:9fa600030013c4de8165339db93d182b9431076eb98eb40ee068700c9c813e34", 712 | "sha256:a80a78046a72361de73f8f395f1f1e49f956c6be882eed58505a15f3e430962b", 713 | "sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c", 714 | "sha256:b5b9eccad747aabaaffbc6064800670f0c297e52c12754eb1d976c57e4f74dcb", 715 | "sha256:c5687b8d43cf58545ade1fe3e055f70eac7a5a1a0bf42824308d868289a95737", 716 | "sha256:cba8c411ef271aa037d7357a2bc8f9ee8b58b9965831d9e51baf703280dc73d3", 717 | "sha256:d15a181d1ecd0d4270dc32edb46f7cb7733c7c508857278d3d378d14d606db2d", 718 | "sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53", 719 | "sha256:d4eccecf9adf6fbcc6861a38015c2a64f38b9d94838ac1810a9023a0609e1b78", 720 | "sha256:d67d839ede4ed1b28a4e8909735fc992a923cdb84e618544973d7dfc71540803", 721 | "sha256:daf496c58a8c52083df09b80c860005194014c3698698d1a57cbcfa182142a3a", 722 | "sha256:e61ceaab6f49fb8bdfaa0f92c4b57bcfbea54c09277b1b4f7ac376bfb7a7c174", 723 | "sha256:f84fbc98b019fef2ee9a1cb3ce93e3187a6df0b2538a651bfb890254ba9f90b5" 724 | ], 725 | "version": "==6.0" 726 | }, 727 | "requests": { 728 | "hashes": [ 729 | "sha256:7c5599b102feddaa661c826c56ab4fee28bfd17f5abca1ebbe3e7f19d7c97983", 730 | "sha256:8fefa2a1a1365bf5520aac41836fbee479da67864514bdb821f31ce07ce65349" 731 | ], 732 | "version": "==2.28.1" 733 | }, 734 | "rsa": { 735 | "hashes": [ 736 | "sha256:90260d9058e514786967344d0ef75fa8727eed8a7d2e43ce9f4bcf1b536174f7", 737 | "sha256:e38464a49c6c85d7f1351b0126661487a7e0a14a50f1675ec50eb34d4f20ef21" 738 | ], 739 | "markers": "python_version >= '3.6' and python_version < '4'", 740 | "version": "==4.9" 741 | }, 742 | "six": { 743 | "hashes": [ 744 | "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926", 745 | "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254" 746 | ], 747 | "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", 748 | "version": "==1.16.0" 749 | }, 750 | "sniffio": { 751 | "hashes": [ 752 | "sha256:e60305c5e5d314f5389259b7f22aaa33d8f7dee49763119234af3755c55b9101", 753 | "sha256:eecefdce1e5bbfb7ad2eeaabf7c1eeb404d7757c379bd1f7e5cce9d8bf425384" 754 | ], 755 | "markers": "python_version >= '3.7'", 756 | "version": "==1.3.0" 757 | }, 758 | "sqlalchemy": { 759 | "hashes": [ 760 | "sha256:0002e829142b2af00b4eaa26c51728f3ea68235f232a2e72a9508a3116bd6ed0", 761 | "sha256:0005bd73026cd239fc1e8ccdf54db58b6193be9a02b3f0c5983808f84862c767", 762 | "sha256:0292f70d1797e3c54e862e6f30ae474014648bc9c723e14a2fda730adb0a9791", 763 | "sha256:036d8472356e1d5f096c5e0e1a7e0f9182140ada3602f8fff6b7329e9e7cfbcd", 764 | "sha256:05f0de3a1dc3810a776275763764bb0015a02ae0f698a794646ebc5fb06fad33", 765 | "sha256:0990932f7cca97fece8017414f57fdd80db506a045869d7ddf2dda1d7cf69ecc", 766 | "sha256:13e397a9371ecd25573a7b90bd037db604331cf403f5318038c46ee44908c44d", 767 | "sha256:14576238a5f89bcf504c5f0a388d0ca78df61fb42cb2af0efe239dc965d4f5c9", 768 | "sha256:199a73c31ac8ea59937cc0bf3dfc04392e81afe2ec8a74f26f489d268867846c", 769 | "sha256:2082a2d2fca363a3ce21cfa3d068c5a1ce4bf720cf6497fb3a9fc643a8ee4ddd", 770 | "sha256:22ff16cedab5b16a0db79f1bc99e46a6ddececb60c396562e50aab58ddb2871c", 771 | "sha256:2307495d9e0ea00d0c726be97a5b96615035854972cc538f6e7eaed23a35886c", 772 | "sha256:2ad2b727fc41c7f8757098903f85fafb4bf587ca6605f82d9bf5604bd9c7cded", 773 | "sha256:2d6495f84c4fd11584f34e62f9feec81bf373787b3942270487074e35cbe5330", 774 | "sha256:361f6b5e3f659e3c56ea3518cf85fbdae1b9e788ade0219a67eeaaea8a4e4d2a", 775 | "sha256:3e2ef592ac3693c65210f8b53d0edcf9f4405925adcfc031ff495e8d18169682", 776 | "sha256:4676d51c9f6f6226ae8f26dc83ec291c088fe7633269757d333978df78d931ab", 777 | "sha256:4ba7e122510bbc07258dc42be6ed45997efdf38129bde3e3f12649be70683546", 778 | "sha256:5102fb9ee2c258a2218281adcb3e1918b793c51d6c2b4666ce38c35101bb940e", 779 | "sha256:5323252be2bd261e0aa3f33cb3a64c45d76829989fa3ce90652838397d84197d", 780 | "sha256:58bb65b3274b0c8a02cea9f91d6f44d0da79abc993b33bdedbfec98c8440175a", 781 | "sha256:59bdc291165b6119fc6cdbc287c36f7f2859e6051dd923bdf47b4c55fd2f8bd0", 782 | "sha256:5facb7fd6fa8a7353bbe88b95695e555338fb038ad19ceb29c82d94f62775a05", 783 | "sha256:639e1ae8d48b3c86ffe59c0daa9a02e2bfe17ca3d2b41611b30a0073937d4497", 784 | "sha256:8eb8897367a21b578b26f5713833836f886817ee2ffba1177d446fa3f77e67c8", 785 | "sha256:90484a2b00baedad361402c257895b13faa3f01780f18f4a104a2f5c413e4536", 786 | "sha256:9c56e19780cd1344fcd362fd6265a15f48aa8d365996a37fab1495cae8fcd97d", 787 | "sha256:b67fc780cfe2b306180e56daaa411dd3186bf979d50a6a7c2a5b5036575cbdbb", 788 | "sha256:c0dcf127bb99458a9d211e6e1f0f3edb96c874dd12f2503d4d8e4f1fd103790b", 789 | "sha256:c23d64a0b28fc78c96289ffbd0d9d1abd48d267269b27f2d34e430ea73ce4b26", 790 | "sha256:ccfd238f766a5bb5ee5545a62dd03f316ac67966a6a658efb63eeff8158a4bbf", 791 | "sha256:cd767cf5d7252b1c88fcfb58426a32d7bd14a7e4942497e15b68ff5d822b41ad", 792 | "sha256:ce8feaa52c1640de9541eeaaa8b5fb632d9d66249c947bb0d89dd01f87c7c288", 793 | "sha256:d2e054aed4645f9b755db85bc69fc4ed2c9020c19c8027976f66576b906a74f1", 794 | "sha256:e16c2be5cb19e2c08da7bd3a87fed2a0d4e90065ee553a940c4fc1a0fb1ab72b", 795 | "sha256:e4b12e3d88a8fffd0b4ca559f6d4957ed91bd4c0613a4e13846ab8729dc5c251", 796 | "sha256:e570cfc40a29d6ad46c9aeaddbdcee687880940a3a327f2c668dd0e4ef0a441d", 797 | "sha256:eb30cf008850c0a26b72bd1b9be6730830165ce049d239cfdccd906f2685f892", 798 | "sha256:f37fa70d95658763254941ddd30ecb23fc4ec0c5a788a7c21034fc2305dab7cc", 799 | "sha256:f5ebeeec5c14533221eb30bad716bc1fd32f509196318fb9caa7002c4a364e4c", 800 | "sha256:f5fa526d027d804b1f85cdda1eb091f70bde6fb7d87892f6dd5a48925bc88898" 801 | ], 802 | "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'", 803 | "version": "==1.4.41" 804 | }, 805 | "sqlalchemy-utils": { 806 | "hashes": [ 807 | "sha256:5c13b5d08adfaa85f3d4e8ec09a75136216fad41346980d02974a70a77988bf9", 808 | "sha256:9f9afba607a40455cf703adfa9846584bf26168a0c5a60a70063b70d65051f4d" 809 | ], 810 | "index": "pypi", 811 | "version": "==0.38.3" 812 | }, 813 | "starlette": { 814 | "hashes": [ 815 | "sha256:5a60c5c2d051f3a8eb546136aa0c9399773a689595e099e0877704d5888279bf", 816 | "sha256:c6d21096774ecb9639acad41b86b7706e52ba3bf1dc13ea4ed9ad593d47e24c7" 817 | ], 818 | "markers": "python_version >= '3.6'", 819 | "version": "==0.19.1" 820 | }, 821 | "tomli": { 822 | "hashes": [ 823 | "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc", 824 | "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f" 825 | ], 826 | "markers": "python_version >= '3.7'", 827 | "version": "==2.0.1" 828 | }, 829 | "typing-extensions": { 830 | "hashes": [ 831 | "sha256:25642c956049920a5aa49edcdd6ab1e06d7e5d467fc00e0506c44ac86fbfca02", 832 | "sha256:e6d2677a32f47fc7eb2795db1dd15c1f34eff616bcaf2cfb5e997f854fa1c4a6" 833 | ], 834 | "markers": "python_version < '3.10'", 835 | "version": "==4.3.0" 836 | }, 837 | "ujson": { 838 | "hashes": [ 839 | "sha256:025758cf6561af6986d77cd4af9367ab56dde5c7c50f13f59e6964b4b25df73e", 840 | "sha256:0551c1ba0bc9e05b69d9c18266dbc93252b5fa3cd9940051bc88a0dd33607b19", 841 | "sha256:05e411627e5d6ee773232960ca7307e66017f78e3fa74f7e95c3a8cc5cb05415", 842 | "sha256:0b46aee21e5d75426c4058dfdb42f7e7b1d130c664ee5027a8dbbc50872dc32b", 843 | "sha256:0bcde3135265ecdd5714a7de4fdc167925390d7b17ca325e59980f4114c962b8", 844 | "sha256:1120c8263f7d85e89533a2b46d80cc6def15114772010ede4d197739e111dba6", 845 | "sha256:13297a7d501f9c8c53e409d4fa57cc574e4fbfbe8807ef2c4c7ce2e3ec933a85", 846 | "sha256:191f88d5865740497b9827ef9b7c12f37a79872ac984e09f0901a10024019380", 847 | "sha256:1a2e645325f844f9c890c9d956fc2d35ca91f38c857278238ef6516c2f99cf7c", 848 | "sha256:2974b17bc522ef86d98b498959d82f03c02e07d9eb08746026415298f4a4bca3", 849 | "sha256:2d98248f1df1e1aab67e0374ab98945dd36bc1764753d71fd8aea5f296360b76", 850 | "sha256:31bdb6d771d5ef6d37134b42211500bfe176c55d399f3317e569783dc42ed38e", 851 | "sha256:3212847d3885bfd4f5fd56cdc37645a8f8e8a80d6cb569505da22fd9eb0e1a02", 852 | "sha256:326a96324ed9215b0bc9f1a5af324fb33900b6b0901516bcc421475d6596de0d", 853 | "sha256:381c97d326d1ec569d318cc0ae83940ea2df125ede1000871680fefd5b7fdea9", 854 | "sha256:39bb702ca1612253b5e4b6004e0f20208c98a446606aa351f9a7ba5ceaff0eb8", 855 | "sha256:3a0707f381f97e1287c0dbf94d95bd6c0bbf6e4eeeaa656f0076b7883010c818", 856 | "sha256:400e4ca8a59f71398e8fa56c4d2d6f535e2a121ddb57284ec15752ffce2dd63a", 857 | "sha256:422653083c6df6cec17fdb5d6106c209aad9b0c94131c53b073980403db22167", 858 | "sha256:511aa641a5b91d19280183b134fb6c473039d4dd82e987ac810cffba783521ac", 859 | "sha256:5df8b6369ee5ee2685fcc917f6c46b34e599c6e9a512fada6dfd752b909fa06a", 860 | "sha256:67f4e2fa81e1d99c01e7b1978ab0cbf3c9a8b663f683a709f87baad110d5b940", 861 | "sha256:68c7f753aec490c6566fd3cd301887c413ac3a588316e446f30a4134ac665668", 862 | "sha256:6a20f2f6e8818c1ab89dd4be6bbad3fc2ddb15287f89e7ea35f3eb849afebbd9", 863 | "sha256:6b953e09441e307504130755e5bd6b15850178d591f66292bba4608c4f7f9b00", 864 | "sha256:754f422aba8db8201a1073f25e2f732effc6471f8755708b16e6ebf19dd23634", 865 | "sha256:784dbd12925845a3f0757a956447e2fd31418abb5aeaebf3aca1203195f16fd1", 866 | "sha256:7d4c9ccd30e621e714ec24ca911ad8873567dc1ac1e5e914405ea9dd16b9d40c", 867 | "sha256:7e12272361e9722777c83b3f5b0bb91d402531f36e80c6e5fafb6acb89e897e3", 868 | "sha256:8cce79ce47c37132373fbdf55b683883c262a3a60763130e080b8394c1201d32", 869 | "sha256:8cd6117e33233f2de6bc896eea6a5a59b58a37db08f371157264e0ec5e51c76a", 870 | "sha256:8d472efa9c92e1b2933a22d2f1dbd5237087997136b24ac2b913bf4e8be03135", 871 | "sha256:91edcf9978ee401119e9c8589376ae37fd3e6e75ee365c49385cb005eaff1535", 872 | "sha256:9ae1d0094ce730e39e09656bc14074d9573cdd80adec1a55b06d8bf1f9613a01", 873 | "sha256:aa00b746138835271653b0c3da171d2a8b510c579381f71e8b8e03484d50d825", 874 | "sha256:aaa77af91df3f71858a1f792c74d3f2d3abf3875f93ab1a2b9a24b3797743b02", 875 | "sha256:b045ca5497a950cc3492840adb3bcb3b9e305ed6599ed14c6aeaa08011aa463f", 876 | "sha256:b40a3757a563ef77c3f2f9ea1732c2924e8b3b2bda3fa89513f949472ad40b6e", 877 | "sha256:baa76a6f707a6d22437fe9c7ec9719672fb04d4d9435a3e80ee9b1aaeb2089d9", 878 | "sha256:cec010d318a0238b1333ea9f40d5603d374cc026c29c4471e2661712c6682da1", 879 | "sha256:dd0d4ec694cab8a0a4d85f45f81ae0065465c4670f0db72ba48d6c4e7ae42834", 880 | "sha256:e2a9ddb5c6d1427056b8d62a1a172a18ae522b14d9ba5996b8281b09cba87edd", 881 | "sha256:e844be0831042aa91e847e5ab03bddd1089ab1a8dd0a1bf90411abf864f058b2", 882 | "sha256:e91947fda8354ea7faf698b084ebcdbabd239e7b15d8436fb74394f59a207ac9", 883 | "sha256:ea7fbc540bc04d5b05e5cd54e60ee8745ac665eedf2bad2ba9d12d5c7a7b7d2e", 884 | "sha256:ee29cf5cfc1e841708297633e1ce749aa851fb96830bbe51f2e5940741ff2441", 885 | "sha256:ef985eb2770900a485431910bd3f333b56d1a34b65f8c26a6ed8e8adf55f98d9", 886 | "sha256:f5c547d49a7e9d3f231e9323171bbbbcef63173fb007a2787cd4f05ac6269315", 887 | "sha256:fbea46c0fbc1c3bc8f957afd8dbb25b4ea3a356e18ee6dd79ace6cf32bd4cff7", 888 | "sha256:fd82932aaa224abd7d01e823b77aef9970f5ac1695027331d99e7f5fda9d37f5" 889 | ], 890 | "version": "==5.4.0" 891 | }, 892 | "urllib3": { 893 | "hashes": [ 894 | "sha256:3fa96cf423e6987997fc326ae8df396db2a8b7c667747d47ddd8ecba91f4a74e", 895 | "sha256:b930dd878d5a8afb066a637fbb35144fe7901e3b209d1cd4f524bd0e9deee997" 896 | ], 897 | "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5' and python_version < '4'", 898 | "version": "==1.26.12" 899 | }, 900 | "uvicorn": { 901 | "extras": [ 902 | "standard" 903 | ], 904 | "hashes": [ 905 | "sha256:19e2a0e96c9ac5581c01eb1a79a7d2f72bb479691acd2b8921fce48ed5b961a6", 906 | "sha256:5180f9d059611747d841a4a4c4ab675edf54c8489e97f96d0583ee90ac3bfc23" 907 | ], 908 | "version": "==0.17.6" 909 | }, 910 | "uvloop": { 911 | "hashes": [ 912 | "sha256:04ff57aa137230d8cc968f03481176041ae789308b4d5079118331ab01112450", 913 | "sha256:089b4834fd299d82d83a25e3335372f12117a7d38525217c2258e9b9f4578897", 914 | "sha256:1e5f2e2ff51aefe6c19ee98af12b4ae61f5be456cd24396953244a30880ad861", 915 | "sha256:30ba9dcbd0965f5c812b7c2112a1ddf60cf904c1c160f398e7eed3a6b82dcd9c", 916 | "sha256:3a19828c4f15687675ea912cc28bbcb48e9bb907c801873bd1519b96b04fb805", 917 | "sha256:6224f1401025b748ffecb7a6e2652b17768f30b1a6a3f7b44660e5b5b690b12d", 918 | "sha256:647e481940379eebd314c00440314c81ea547aa636056f554d491e40503c8464", 919 | "sha256:6ccd57ae8db17d677e9e06192e9c9ec4bd2066b77790f9aa7dede2cc4008ee8f", 920 | "sha256:772206116b9b57cd625c8a88f2413df2fcfd0b496eb188b82a43bed7af2c2ec9", 921 | "sha256:8e0d26fa5875d43ddbb0d9d79a447d2ace4180d9e3239788208527c4784f7cab", 922 | "sha256:98d117332cc9e5ea8dfdc2b28b0a23f60370d02e1395f88f40d1effd2cb86c4f", 923 | "sha256:b572256409f194521a9895aef274cea88731d14732343da3ecdb175228881638", 924 | "sha256:bd53f7f5db562f37cd64a3af5012df8cac2c464c97e732ed556800129505bd64", 925 | "sha256:bd8f42ea1ea8f4e84d265769089964ddda95eb2bb38b5cbe26712b0616c3edee", 926 | "sha256:e814ac2c6f9daf4c36eb8e85266859f42174a4ff0d71b99405ed559257750382", 927 | "sha256:f74bc20c7b67d1c27c72601c78cf95be99d5c2cdd4514502b4f3eb0933ff1228" 928 | ], 929 | "version": "==0.16.0" 930 | }, 931 | "watchgod": { 932 | "hashes": [ 933 | "sha256:2f3e8137d98f493ff58af54ea00f4d1433a6afe2ed08ab331a657df468c6bfce", 934 | "sha256:cb11ff66657befba94d828e3b622d5fb76f22fbda1376f355f3e6e51e97d9450" 935 | ], 936 | "version": "==0.8.2" 937 | }, 938 | "websockets": { 939 | "hashes": [ 940 | "sha256:07cdc0a5b2549bcfbadb585ad8471ebdc7bdf91e32e34ae3889001c1c106a6af", 941 | "sha256:210aad7fdd381c52e58777560860c7e6110b6174488ef1d4b681c08b68bf7f8c", 942 | "sha256:28dd20b938a57c3124028680dc1600c197294da5db4292c76a0b48efb3ed7f76", 943 | "sha256:2f94fa3ae454a63ea3a19f73b95deeebc9f02ba2d5617ca16f0bbdae375cda47", 944 | "sha256:31564a67c3e4005f27815634343df688b25705cccb22bc1db621c781ddc64c69", 945 | "sha256:347974105bbd4ea068106ec65e8e8ebd86f28c19e529d115d89bd8cc5cda3079", 946 | "sha256:379e03422178436af4f3abe0aa8f401aa77ae2487843738542a75faf44a31f0c", 947 | "sha256:3eda1cb7e9da1b22588cefff09f0951771d6ee9fa8dbe66f5ae04cc5f26b2b55", 948 | "sha256:51695d3b199cd03098ae5b42833006a0f43dc5418d3102972addc593a783bc02", 949 | "sha256:54c000abeaff6d8771a4e2cef40900919908ea7b6b6a30eae72752607c6db559", 950 | "sha256:5b936bf552e4f6357f5727579072ff1e1324717902127ffe60c92d29b67b7be3", 951 | "sha256:6075fd24df23133c1b078e08a9b04a3bc40b31a8def4ee0b9f2c8865acce913e", 952 | "sha256:661f641b44ed315556a2fa630239adfd77bd1b11cb0b9d96ed8ad90b0b1e4978", 953 | "sha256:6ea6b300a6bdd782e49922d690e11c3669828fe36fc2471408c58b93b5535a98", 954 | "sha256:6ed1d6f791eabfd9808afea1e068f5e59418e55721db8b7f3bfc39dc831c42ae", 955 | "sha256:7934e055fd5cd9dee60f11d16c8d79c4567315824bacb1246d0208a47eca9755", 956 | "sha256:7ab36e17af592eec5747c68ef2722a74c1a4a70f3772bc661079baf4ae30e40d", 957 | "sha256:7f6d96fdb0975044fdd7953b35d003b03f9e2bcf85f2d2cf86285ece53e9f991", 958 | "sha256:83e5ca0d5b743cde3d29fda74ccab37bdd0911f25bd4cdf09ff8b51b7b4f2fa1", 959 | "sha256:85506b3328a9e083cc0a0fb3ba27e33c8db78341b3eb12eb72e8afd166c36680", 960 | "sha256:8af75085b4bc0b5c40c4a3c0e113fa95e84c60f4ed6786cbb675aeb1ee128247", 961 | "sha256:8b1359aba0ff810d5830d5ab8e2c4a02bebf98a60aa0124fb29aa78cfdb8031f", 962 | "sha256:8fbd7d77f8aba46d43245e86dd91a8970eac4fb74c473f8e30e9c07581f852b2", 963 | "sha256:907e8247480f287aa9bbc9391bd6de23c906d48af54c8c421df84655eef66af7", 964 | "sha256:93d5ea0b5da8d66d868b32c614d2b52d14304444e39e13a59566d4acb8d6e2e4", 965 | "sha256:97bc9d41e69a7521a358f9b8e44871f6cdeb42af31815c17aed36372d4eec667", 966 | "sha256:994cdb1942a7a4c2e10098d9162948c9e7b235df755de91ca33f6e0481366fdb", 967 | "sha256:a141de3d5a92188234afa61653ed0bbd2dde46ad47b15c3042ffb89548e77094", 968 | "sha256:a1e15b230c3613e8ea82c9fc6941b2093e8eb939dd794c02754d33980ba81e36", 969 | "sha256:aad5e300ab32036eb3fdc350ad30877210e2f51bceaca83fb7fef4d2b6c72b79", 970 | "sha256:b529fdfa881b69fe563dbd98acce84f3e5a67df13de415e143ef053ff006d500", 971 | "sha256:b9c77f0d1436ea4b4dc089ed8335fa141e6a251a92f75f675056dac4ab47a71e", 972 | "sha256:bb621ec2dbbbe8df78a27dbd9dd7919f9b7d32a73fafcb4d9252fc4637343582", 973 | "sha256:c7250848ce69559756ad0086a37b82c986cd33c2d344ab87fea596c5ac6d9442", 974 | "sha256:c8d1d14aa0f600b5be363077b621b1b4d1eb3fbf90af83f9281cda668e6ff7fd", 975 | "sha256:d1655a6fc7aecd333b079d00fb3c8132d18988e47f19740c69303bf02e9883c6", 976 | "sha256:d6353ba89cfc657a3f5beabb3b69be226adbb5c6c7a66398e17809b0ce3c4731", 977 | "sha256:da4377904a3379f0c1b75a965fff23b28315bcd516d27f99a803720dfebd94d4", 978 | "sha256:e49ea4c1a9543d2bd8a747ff24411509c29e4bdcde05b5b0895e2120cb1a761d", 979 | "sha256:e4e08305bfd76ba8edab08dcc6496f40674f44eb9d5e23153efa0a35750337e8", 980 | "sha256:e6fa05a680e35d0fcc1470cb070b10e6fe247af54768f488ed93542e71339d6f", 981 | "sha256:e7e6f2d6fd48422071cc8a6f8542016f350b79cc782752de531577d35e9bd677", 982 | "sha256:e904c0381c014b914136c492c8fa711ca4cced4e9b3d110e5e7d436d0fc289e8", 983 | "sha256:ec2b0ab7edc8cd4b0eb428b38ed89079bdc20c6bdb5f889d353011038caac2f9", 984 | "sha256:ef5ce841e102278c1c2e98f043db99d6755b1c58bde475516aef3a008ed7f28e", 985 | "sha256:f351c7d7d92f67c0609329ab2735eee0426a03022771b00102816a72715bb00b", 986 | "sha256:fab7c640815812ed5f10fbee7abbf58788d602046b7bb3af9b1ac753a6d5e916", 987 | "sha256:fc06cc8073c8e87072138ba1e431300e2d408f054b27047d047b549455066ff4" 988 | ], 989 | "version": "==10.3" 990 | } 991 | }, 992 | "develop": {} 993 | } 994 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # FastAPI Template 2 | A FastAPI template with Redis, Docker and PostgreSQL 3 | 4 | ## Introduction 5 | This FastAPI template was created out of a need for consistent structure for projects with a setup that's easy to understand and use with as minimal additional setup as possible. 6 | 7 | ### What is hoped to be achieved 8 | - Simplicity; no need for bloated classes and huge utils that you'll likely never use. Everything here should be very much needed in most projects 9 | - Low barrier for entry; a template that is easy to start with and does not seem too advanced for beginners to use 10 | - Production-ready; the above does not remove the important fact that this should be always production-ready without obvious faults 11 | - Consistency; a smell for codebases is you not having a 'knowing' of where certain code is located. A wanted structure is one that's easy to navigate 12 | 13 | ### What is not hoped to be achieved 14 | - Batteries-included; this template, whilst influenced by Django, does not aim to provide Django-like capabilities or to have a 'Django, but for FastAPI' setup 15 | - Utility dump; this template will not be where all sorts of utilities are dumped because, 'why not?'. 16 | 17 | 18 | ## How to use 19 | - Copy `.example.env` into a file, `.env` 20 | - Run `docker-compose up --build` to build and run container. Yes, I expect you to use Docker in this day and age. 21 | 22 | The above is enough to run your project except you need more than that if you are working on a real application. So, this: 23 | 24 | ### Personalizing the template 25 | First, head to `app/settings` and edit `Settings.APP_NAME` to reflect the name of your application. 26 | 27 | Next, you'll want to run `pipenv install` to ensure you get a Python environment you can attach to your IDE to allow for autocomplete and auto-imports so you don't have yellow and red lines everywhere 28 | 29 | After this, we head to the .env to edit some of the values to your taste. Each value is explained in the [Config section](#config). 30 | 31 | ### Databases 32 | Databases can be quite dicey and I'm happy to say Alembic is used to handle migrations and whatnots. This coupled with SQLAlchemy makes the world a better place. Whilst PostgreSQL is assumed to be the default database. You can of course edit things to your liking. 33 | 34 | - Create migrations with `docker-compose run web alembic revision -m "Migration message here"`. 35 | - Run migrations within Docker with `docker-compose run web alembic upgrade head`. 36 | 37 | 38 | ## Config 39 | This section documents configuration options and the meaning of settings values and how to use them. 40 | 41 | ### Environmental Variables 42 | The environmental variables in the [.example.env](./.example.env) file have specific purposes: 43 | |Key | Description| Default 44 | |---|---|---| 45 | |ALLOWED_HOST|The domain you intend to run this application on|0.0.0.0| 46 | |SECRET_KEY|A secret value used to hash and sign tokens and other security-related stuffs. | meandyouaretogether| 47 | |DEBUG|A value that evaluates to a boolean to determine whether the app is run in debug mode or production.|True| 48 | |PORT|Port the application will run on. If you change this from default, you will have to change the value in the [docker-compose.yml](./docker-compose.yml) file.|11000| 49 | |POSTGRES_USER|This is the default user for the PostgreSQL database. On first run, Docker will use this value to initialize a postgres user so you'll want to set it before your first execution.|sasori| 50 | |POSTGRES_PASSWORD|This is the default password for the PostgreSQL database. On first run, Docker will use this value to initialize a postgres database so you'll want to set it before your first execution.|sasori| 51 | |POSTGRES_DB|This is the default PostgreSQL database created. On first run, Docker will use this value to initialize a postgres database so you'll want to set it before your first execution|akatsuki| 52 | |POSTGRES_TEST_DB|This is the database created for test cases. It is flushed after every test case is run.| hebi 53 | |POSTGRES_PORT|The port Postgres will run on. Don't change it except you know what you're doing, and if you do, change the value in the [docker-compose.yml](./docker-compose.yml) file.|5432| 54 | |POSTGRES_HOST|The host set in the docker container where the PostgreSQL instance will be running. Don't change it except you know what you're doing, and if you do, change the value in the [docker-compose.yml](./docker-compose.yml) file.|postgres| 55 | |REDIS_HOST|The host set in the docker container where the Redis instance will be running. Don't change it except you know what you're doing, and if you do, change the value in the [docker-compose.yml](./docker-compose.yml) file.|redis| 56 | |REDIS_PORT|The port the application will use to connect to Redis. Don't change it except you know what you're doing, and if you do, change the value in the [docker-compose.yml](./docker-compose.yml) file.|6379| 57 | 58 | ### Application Settings 59 | Application settings are set in the `app.settings.Settings` class and are used to store app wide configurations. Values are so: 60 | 61 | |key|Description|Default| 62 | |---|---|---| 63 | |APP_TITLE|Name of the application, will show in the documentation|App Name| 64 | |ALLOWED_HOST|The domain you intend to run this application on|Derived from `.env` with key `ALLOWED_HOST`| 65 | |SECRET_KEY|A secret value used to hash and sign tokens and other security-related stuffs.|Derived from `.env` with key `SECRET_KEY`| 66 | |DEBUG|A value that evaluates to a boolean to determine whether the app is run in debug mode or production.|Derived from `.env` with key `DEBUG`| 67 | |ALLOWED_PORT|Port the application will run on. If you change this from default, you will have to change the value in the [docker-compose.yml](./docker-compose.yml) file.|Derived from `.env` with key `PORT`| 68 | |DB_USER|This is the default user for the PostgreSQL database. On first run, Docker will use this value to initialize a postgres user so you'll want to set it before your first execution.|Derived from `.env` with key `POSTGRES_USER`| 69 | |DB_PASSWORD|This is the default password for the PostgreSQL database. On first run, Docker will use this value to initialize a postgres database so you'll want to set it before your first execution.|Derived from `.env` with key `POSTGRES_PASSWORD`| 70 | |DB_DB|This is the default PostgreSQL database created. On first run, Docker will use this value to initialize a postgres database so you'll want to set it before your first execution|Derived from `.env` with key `POSTGRES_DB`| 71 | |DB_PORT|The port Postgres will run on. Don't change it except you know what you're doing, and if you do, change the value in the [docker-compose.yml](./docker-compose.yml) file.|Derived from `.env` with key `POSTGRES_PORT`| 72 | |DB_HOST|The host set in the docker container where the PostgreSQL instance will be running. Don't change it except you know what you're doing, and if you do, change the value in the [docker-compose.yml](./docker-compose.yml) file.|Derived from `.env` with key `POSTGRES_HOST`| 73 | |DB_URL|URL leading to the application database|`postgresql+psycopg2://{DB_USER}:{DB_PASSWORD}@{DB_HOST}:{DB_PORT}/{DB_DB}`| 74 | |TEST_DB|This is the database created for test cases. It is flushed after every test case is run.|Derived from `.env` with key `POSTGRES_TEST_DB` 75 | |TEST_DB_URL|URL leading to test database|`postgresql+psycopg2://{DB_USER}:{DB_PASSWORD}@{DB_HOST}:{DB_PORT}/{TEST_DB}`| 76 | |ACCESS_TOKEN_EXPIRY_TIME|Duration in seconds before access token expires| 60 * 30 (seconds)| 77 | |REFRESH_TOKEN_EXPIRY_TIME|Duration in seconds before refresh token expires| 60 * 30 (seconds)| 78 | |PASSWORD_HASHER|Hashing algorithm for passwords|`CryptContext(schemes=["bcrypt"], deprecated="auto")`| 79 | |JWT_ALGORITHM|Algorithm used to generate JWT token|HS256| 80 | |REDIS_HOST|The host set in the docker container where the Redis instance will be running. Don't change it except you know what you're doing, and if you do, change the value in the [docker-compose.yml](./docker-compose.yml) file.|Derived from `.env` with key `REDIS_HOST`| 81 | |REDIS_PORT|The port the application will use to connect to Redis. Don't change it except you know what you're doing, and if you do, change the value in the [docker-compose.yml](./docker-compose.yml) file.|Derived from `.env` with key `REDIS_HOST`| 82 | |PAGE_SIZE|[Not Implemented] default size of page for paginated items|50 83 | 84 | ## I don't want some things 85 | There is the possibility that you may not want some features such as Redis. You can easily remove what you don't need and proceed as planned. 86 | -------------------------------------------------------------------------------- /alembic.ini: -------------------------------------------------------------------------------- 1 | # A generic, single database configuration. 2 | 3 | [alembic] 4 | # path to migration scripts 5 | script_location = alembic 6 | 7 | # template used to generate migration file names; The default value is %%(rev)s_%%(slug)s 8 | # Uncomment the line below if you want the files to be prepended with date and time 9 | # see https://alembic.sqlalchemy.org/en/latest/tutorial.html#editing-the-ini-file 10 | # for all available tokens 11 | # file_template = %%(year)d_%%(month).2d_%%(day).2d_%%(hour).2d%%(minute).2d-%%(rev)s_%%(slug)s 12 | 13 | # sys.path path, will be prepended to sys.path if present. 14 | # defaults to the current working directory. 15 | prepend_sys_path = . 16 | 17 | # timezone to use when rendering the date within the migration file 18 | # as well as the filename. 19 | # If specified, requires the python-dateutil library that can be 20 | # installed by adding `alembic[tz]` to the pip requirements 21 | # string value is passed to dateutil.tz.gettz() 22 | # leave blank for localtime 23 | # timezone = 24 | 25 | # max length of characters to apply to the 26 | # "slug" field 27 | # truncate_slug_length = 40 28 | 29 | # set to 'true' to run the environment during 30 | # the 'revision' command, regardless of autogenerate 31 | # revision_environment = false 32 | 33 | # set to 'true' to allow .pyc and .pyo files without 34 | # a source .py file to be detected as revisions in the 35 | # versions/ directory 36 | # sourceless = false 37 | 38 | # version location specification; This defaults 39 | # to alembic/versions. When using multiple version 40 | # directories, initial revisions must be specified with --version-path. 41 | # The path separator used here should be the separator specified by "version_path_separator" below. 42 | # version_locations = %(here)s/bar:%(here)s/bat:alembic/versions 43 | 44 | # version path separator; As mentioned above, this is the character used to split 45 | # version_locations. The default within new alembic.ini files is "os", which uses os.pathsep. 46 | # If this key is omitted entirely, it falls back to the legacy behavior of splitting on spaces and/or commas. 47 | # Valid values for version_path_separator are: 48 | # 49 | # version_path_separator = : 50 | # version_path_separator = ; 51 | # version_path_separator = space 52 | version_path_separator = os # Use os.pathsep. Default configuration used for new projects. 53 | 54 | # the output encoding used when revision files 55 | # are written from script.py.mako 56 | # output_encoding = utf-8 57 | 58 | sqlalchemy.url = driver://user:pass@localhost/dbname 59 | 60 | 61 | [post_write_hooks] 62 | # post_write_hooks defines scripts or Python functions that are run 63 | # on newly generated revision scripts. See the documentation for further 64 | # detail and examples 65 | 66 | # format using "black" - use the console_scripts runner, against the "black" entrypoint 67 | # hooks = black 68 | # black.type = console_scripts 69 | # black.entrypoint = black 70 | # black.options = -l 79 REVISION_SCRIPT_FILENAME 71 | 72 | # Logging configuration 73 | [loggers] 74 | keys = root,sqlalchemy,alembic 75 | 76 | [handlers] 77 | keys = console 78 | 79 | [formatters] 80 | keys = generic 81 | 82 | [logger_root] 83 | level = WARN 84 | handlers = console 85 | qualname = 86 | 87 | [logger_sqlalchemy] 88 | level = WARN 89 | handlers = 90 | qualname = sqlalchemy.engine 91 | 92 | [logger_alembic] 93 | level = INFO 94 | handlers = 95 | qualname = alembic 96 | 97 | [handler_console] 98 | class = StreamHandler 99 | args = (sys.stderr,) 100 | level = NOTSET 101 | formatter = generic 102 | 103 | [formatter_generic] 104 | format = %(levelname)-5.5s [%(name)s] %(message)s 105 | datefmt = %H:%M:%S 106 | -------------------------------------------------------------------------------- /alembic/README: -------------------------------------------------------------------------------- 1 | Generic single-database configuration. -------------------------------------------------------------------------------- /alembic/env.py: -------------------------------------------------------------------------------- 1 | from logging.config import fileConfig 2 | 3 | from sqlalchemy import engine_from_config 4 | from sqlalchemy import pool 5 | 6 | from alembic import context 7 | 8 | from app.settings import settings 9 | 10 | # this is the Alembic Config object, which provides 11 | # access to the values within the .ini file in use. 12 | config = context.config 13 | 14 | # this will overwrite the DB url for sqlalchemy 15 | config.set_main_option("sqlalchemy.url", settings.DB_URL) 16 | 17 | # Interpret the config file for Python logging. 18 | # This line sets up loggers basically. 19 | if config.config_file_name is not None: 20 | fileConfig(config.config_file_name) 21 | 22 | # add your model's MetaData object here 23 | # for 'autogenerate' support 24 | from app import models 25 | target_metadata = models.Base.metadata 26 | 27 | # other values from the config, defined by the needs of env.py, 28 | # can be acquired: 29 | # my_important_option = config.get_main_option("my_important_option") 30 | # ... etc. 31 | 32 | 33 | def run_migrations_offline() -> None: 34 | """Run migrations in 'offline' mode. 35 | 36 | This configures the context with just a URL 37 | and not an Engine, though an Engine is acceptable 38 | here as well. By skipping the Engine creation 39 | we don't even need a DBAPI to be available. 40 | 41 | Calls to context.execute() here emit the given string to the 42 | script output. 43 | 44 | """ 45 | url = config.get_main_option("sqlalchemy.url") 46 | context.configure( 47 | url=url, 48 | target_metadata=target_metadata, 49 | literal_binds=True, 50 | dialect_opts={"paramstyle": "named"}, 51 | ) 52 | 53 | with context.begin_transaction(): 54 | context.run_migrations() 55 | 56 | 57 | def run_migrations_online() -> None: 58 | """Run migrations in 'online' mode. 59 | 60 | In this scenario we need to create an Engine 61 | and associate a connection with the context. 62 | 63 | """ 64 | connectable = engine_from_config( 65 | config.get_section(config.config_ini_section), 66 | prefix="sqlalchemy.", 67 | poolclass=pool.NullPool, 68 | ) 69 | 70 | with connectable.connect() as connection: 71 | context.configure( 72 | connection=connection, target_metadata=target_metadata 73 | ) 74 | 75 | with context.begin_transaction(): 76 | context.run_migrations() 77 | 78 | 79 | if context.is_offline_mode(): 80 | run_migrations_offline() 81 | else: 82 | run_migrations_online() 83 | -------------------------------------------------------------------------------- /alembic/script.py.mako: -------------------------------------------------------------------------------- 1 | """${message} 2 | 3 | Revision ID: ${up_revision} 4 | Revises: ${down_revision | comma,n} 5 | Create Date: ${create_date} 6 | 7 | """ 8 | from alembic import op 9 | import sqlalchemy as sa 10 | ${imports if imports else ""} 11 | 12 | # revision identifiers, used by Alembic. 13 | revision = ${repr(up_revision)} 14 | down_revision = ${repr(down_revision)} 15 | branch_labels = ${repr(branch_labels)} 16 | depends_on = ${repr(depends_on)} 17 | 18 | 19 | def upgrade() -> None: 20 | ${upgrades if upgrades else "pass"} 21 | 22 | 23 | def downgrade() -> None: 24 | ${downgrades if downgrades else "pass"} 25 | -------------------------------------------------------------------------------- /app/base.py: -------------------------------------------------------------------------------- 1 | from functools import reduce 2 | from uuid import uuid4 3 | 4 | from sqlalchemy.dialects.postgresql import UUID 5 | from sqlalchemy import Column, MetaData 6 | from sqlalchemy.ext.declarative import as_declarative, declared_attr 7 | from .db import metadata 8 | 9 | 10 | @as_declarative() 11 | class Base: 12 | """ 13 | Base class to handle table schema 14 | """ 15 | __name__: str 16 | 17 | id = Column(UUID(as_uuid=True), primary_key=True, default=uuid4) 18 | 19 | @declared_attr 20 | def metadata(cls) -> MetaData: 21 | return metadata 22 | 23 | @declared_attr 24 | def __tablename__(cls) -> str: 25 | return cls.__name__.lower() 26 | -------------------------------------------------------------------------------- /app/db.py: -------------------------------------------------------------------------------- 1 | import logging 2 | 3 | import sqlalchemy 4 | from databases import Database 5 | from sqlalchemy.orm import sessionmaker 6 | from sqlalchemy_utils import database_exists, create_database 7 | 8 | from .settings import settings 9 | 10 | 11 | db = Database(settings.DB_URL) 12 | metadata = sqlalchemy.MetaData() 13 | engine = sqlalchemy.create_engine(settings.DB_URL, pool_size=3, max_overflow=0) 14 | if not database_exists(engine.url): 15 | create_database(engine.url) 16 | 17 | LocalSession = sessionmaker(autocommit=False, autoflush=False, bind=engine) 18 | 19 | metadata.create_all(engine) 20 | 21 | 22 | def get_db(): 23 | """ 24 | A dependency for working with PostgreSQL 25 | """ 26 | try: 27 | db = LocalSession() 28 | yield db 29 | except Exception as e: 30 | logging.error(e) 31 | finally: 32 | db.close() 33 | -------------------------------------------------------------------------------- /app/models.py: -------------------------------------------------------------------------------- 1 | from .base import Base 2 | from auth.models import * 3 | # add models to table like so: 4 | # from app_name.models import * -------------------------------------------------------------------------------- /app/routes.py: -------------------------------------------------------------------------------- 1 | from fastapi import APIRouter 2 | 3 | from auth.api import router as auth 4 | # add app routes like so: 5 | # from app_name.api import router as app_name 6 | 7 | 8 | router = APIRouter() 9 | router.include_router(auth) 10 | # router.include_router(app_name) -------------------------------------------------------------------------------- /app/schema.py: -------------------------------------------------------------------------------- 1 | from typing import Any 2 | 3 | from fastapi.encoders import jsonable_encoder 4 | from pydantic import BaseModel 5 | 6 | 7 | class BaseSchema(BaseModel): 8 | def to_dict(self) -> Any: 9 | return jsonable_encoder(self) 10 | -------------------------------------------------------------------------------- /app/services.py: -------------------------------------------------------------------------------- 1 | from typing import Type 2 | 3 | from sqlalchemy.orm import Session 4 | 5 | from .base import Base 6 | 7 | 8 | class BaseService: 9 | """ 10 | Base class for services. Handles DB initialization and saving a model 11 | """ 12 | def __init__(self, db: Session) -> None: 13 | self.db = db 14 | 15 | def save(self, model: Type[Base]) -> None: 16 | self.db.add(model) 17 | self.db.commit() 18 | self.db.refresh(model) -------------------------------------------------------------------------------- /app/settings.py: -------------------------------------------------------------------------------- 1 | from os import environ 2 | 3 | from passlib.context import CryptContext 4 | 5 | 6 | class Settings: 7 | APP_TITLE = "App name" 8 | ALLOWED_HOST = environ.get("ALLOWED_HOST") 9 | SECRET_KEY = environ.get("SECRET_KEY") 10 | DEBUG = bool(environ.get("DEBUG")) 11 | ALLOWED_PORT = int(environ.get("PORT")) 12 | DB_USER = environ.get("POSTGRES_USER") 13 | DB_PASSWORD = environ.get("POSTGRES_PASSWORD") 14 | DB_DB = environ.get("POSTGRES_DB") 15 | DB_PORT = environ.get("POSTGRES_PORT") 16 | DB_HOST = environ.get("POSTGRES_HOST") 17 | DB_URL = ( 18 | f"postgresql+psycopg2://{DB_USER}:{DB_PASSWORD}@{DB_HOST}:{DB_PORT}/{DB_DB}" 19 | ) 20 | TEST_DB = environ.get("POSTGRES_TEST_DB") 21 | TEST_DB_URL = ( 22 | f"postgresql+psycopg2://{DB_USER}:{DB_PASSWORD}@{DB_HOST}:{DB_PORT}/{TEST_DB}" 23 | ) 24 | ACCESS_TOKEN_EXPIRY_TIME = 60 * 30 25 | REFRESH_TOKEN_EXPIRY_TIME = 60 * 24 * 365 26 | PASSWORD_HASHER = CryptContext(schemes=["bcrypt"], deprecated="auto") 27 | JWT_ALGORITHM = "HS256" 28 | REDIS_HOST = environ.get("REDIS_HOST", "localhost") 29 | REDIS_PORT = environ.get("REDIS_PORT", "6379") 30 | PAGE_SIZE = 50 31 | 32 | 33 | settings = Settings() -------------------------------------------------------------------------------- /auth/api.py: -------------------------------------------------------------------------------- 1 | from fastapi import APIRouter 2 | 3 | 4 | router = APIRouter(prefix="/auth") 5 | 6 | 7 | @router.get("/ok") 8 | async def ok() -> str: 9 | return "ok" -------------------------------------------------------------------------------- /auth/exceptions.py: -------------------------------------------------------------------------------- 1 | from fastapi import HTTPException, status 2 | 3 | 4 | UserExist = HTTPException( 5 | status_code=status.HTTP_400_BAD_REQUEST, 6 | detail="User with this email already exists", 7 | ) 8 | 9 | InvalidCredentials = HTTPException( 10 | status_code=status.HTTP_401_UNAUTHORIZED, 11 | detail="Email or password is incorrect. Incorrect login credentials", 12 | ) 13 | 14 | TokenExpired = HTTPException( 15 | status_code=status.HTTP_401_UNAUTHORIZED, 16 | detail="Token expired", 17 | headers={"WWW-Authenticate": "Bearer"}, 18 | ) 19 | 20 | UnvalidatedCredentials = HTTPException( 21 | status_code=status.HTTP_403_FORBIDDEN, 22 | detail="Could not validate credentials", 23 | headers={"WWW-Authenticate": "Bearer"}, 24 | ) 25 | 26 | UserDoesNotExist = HTTPException( 27 | status_code=status.HTTP_404_NOT_FOUND, 28 | detail="Could not find user", 29 | ) -------------------------------------------------------------------------------- /auth/models.py: -------------------------------------------------------------------------------- 1 | from typing import Type 2 | 3 | from sqlalchemy import Column, String 4 | 5 | from app.base import Base 6 | 7 | 8 | class User(Base): 9 | """ 10 | Model for user; used for auth and all. 11 | """ 12 | email = Column(String(255), unique=True, index=True, nullable=False) 13 | 14 | @classmethod 15 | def to_dict(cls, instance: Type[Base]): 16 | """ 17 | Additional functionality to convert a User instance into a dictionary 18 | so it can be used as keyword arguments 19 | """ 20 | result = {} 21 | 22 | for column in instance.__table__.columns: 23 | result[column.name] = str(getattr(instance, column.name)) 24 | 25 | return result -------------------------------------------------------------------------------- /auth/schema.py: -------------------------------------------------------------------------------- 1 | from app.schema import BaseSchema 2 | 3 | 4 | class LoginSchema(BaseSchema): 5 | """ 6 | Login the user 7 | """ 8 | email: str 9 | password: str 10 | -------------------------------------------------------------------------------- /auth/services.py: -------------------------------------------------------------------------------- 1 | from datetime import datetime, timedelta 2 | from typing import Any, Dict 3 | 4 | from fastapi.security import OAuth2PasswordBearer 5 | from jose import jwt 6 | 7 | from app.services import BaseService 8 | from app.settings import settings 9 | 10 | # for authentication that works seamlessly with OpenAPI 11 | reuseable_oauth = OAuth2PasswordBearer( 12 | tokenUrl="/api/v1/user/login", 13 | scheme_name="JWT" 14 | ) 15 | 16 | 17 | def hash_password(password: str) -> str: 18 | """Creates a hash of the password""" 19 | hashed_password = settings.PASSWORD_HASHER.hash(password) 20 | return hashed_password 21 | 22 | 23 | def verify_password(plain_password: str, hashed_password: str) -> bool: 24 | """Checks if a password corresponds to a hash""" 25 | password_is_verified = settings.PASSWORD_HASHER.verify( 26 | plain_password, hashed_password 27 | ) 28 | 29 | return bool(password_is_verified) 30 | 31 | 32 | def create_auth_token(payload: Dict[str, Any], expiry: int) -> str: 33 | """ 34 | This handles the encoding of user information into a token 35 | that can be used. It is generic so as to accomodate access and 36 | refresh token. 37 | """ 38 | expiry_delta = datetime.now() + timedelta(seconds=expiry) 39 | data_to_encode = {"expiry": str(expiry_delta), "data": payload} 40 | encoded_data: str = jwt.encode( 41 | data_to_encode, settings.SECRET_KEY, settings.JWT_ALGORITHM 42 | ) 43 | 44 | return encoded_data 45 | 46 | 47 | class UserService(BaseService): 48 | """ 49 | This class helps perform bulk of activities required by the corresponding API 50 | """ 51 | def a_method(self): 52 | """ 53 | Here, the use of `self.save` is demonstrated. 54 | Ensure to take out this method. 55 | """ 56 | model_instance = ... 57 | self.save(model_instance) -------------------------------------------------------------------------------- /auth/tests/test_auth_api.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FastAPI-MEA/fastapi-template/ba8cbdfe04041279ec8dadeb5130f38dd8050d96/auth/tests/test_auth_api.py -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.8' 2 | 3 | services: 4 | web: 5 | build: . 6 | command: python main.py 7 | volumes: 8 | - ./:/usr/src/app/ 9 | ports: 10 | - 11000:11000 11 | env_file: 12 | - ./.env 13 | depends_on: 14 | - postgres 15 | - redis 16 | postgres: 17 | image: postgres 18 | ports: 19 | - 5432:5432 20 | env_file: 21 | - ./.env 22 | redis: 23 | image: redis:alpine 24 | ports: 25 | - 6379:6379 26 | env_file: 27 | - ./.env -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | import logging 2 | 3 | import uvicorn 4 | from fastapi import FastAPI 5 | 6 | from app.routes import router 7 | from app.settings import settings 8 | from app.db import db 9 | 10 | 11 | app = FastAPI(title=settings.APP_TITLE) 12 | 13 | app.include_router(router, prefix="/api/v1") 14 | 15 | 16 | @app.on_event("startup") 17 | async def startup(): 18 | await db.connect() 19 | 20 | 21 | @app.on_event("shutdown") 22 | async def shutdown(): 23 | await db.disconnect() 24 | 25 | 26 | if __name__ == "__main__": 27 | """ 28 | Server configurations 29 | """ 30 | uvicorn.run( 31 | app="main:app", 32 | host=settings.ALLOWED_HOST, 33 | debug=settings.DEBUG, 34 | port=settings.ALLOWED_PORT, 35 | reload=True, 36 | log_level=logging.INFO, 37 | use_colors=True, 38 | ) --------------------------------------------------------------------------------