basic crud NOT SECURE

This commit is contained in:
hay-kot 2021-02-20 20:20:26 -09:00
commit 838ca2a6f8
18 changed files with 311 additions and 14 deletions

View file

@ -2,7 +2,7 @@ import uvicorn
from fastapi import FastAPI
# import utils.startup as startup
from app_config import APP_VERSION, PORT, PRODUCTION, docs_url, redoc_url
from app_config import APP_VERSION, PORT, SECRET, docs_url, redoc_url
from routes import (
backup_routes,
debug_routes,
@ -17,6 +17,7 @@ from routes.recipe import (
recipe_crud_routes,
tag_routes,
)
from routes.users import users
from services.settings_services import default_settings_init
from utils.logger import logger
@ -36,9 +37,12 @@ def start_scheduler():
def init_settings():
default_settings_init()
import services.theme_services
import services.users.users
def api_routers():
# Authentication
app.include_router(users.router)
# Recipes
app.include_router(all_recipe_routes.router)
app.include_router(category_routes.router)
@ -56,7 +60,6 @@ def api_routers():
app.include_router(debug_routes.router)
api_routers()
start_scheduler()
init_settings()

View file

@ -15,9 +15,12 @@ def ensure_dirs():
ENV = CWD.joinpath(".env")
dotenv.load_dotenv(ENV)
SECRET = "super-secret-key"
# General
APP_VERSION = "v0.3.0"
DB_VERSION = "v0.2.1"
DB_VERSION = "v0.3.0"
PRODUCTION = os.environ.get("ENV")
PORT = int(os.getenv("mealie_port", 9000))
API = os.getenv("api_docs", True)

View file

@ -5,6 +5,7 @@ from db.sql.meal_models import MealPlanModel
from db.sql.recipe_models import Category, RecipeModel, Tag
from db.sql.settings_models import SiteSettingsModel
from db.sql.theme_models import SiteThemeModel
from db.sql.users import User
"""
# TODO
@ -54,6 +55,10 @@ class _Themes(BaseDocument):
self.primary_key = "name"
self.sql_model = SiteThemeModel
class _Users(BaseDocument):
def __init__(self) -> None:
self.primary_key = "id"
self.sql_model = User
class Database:
def __init__(self) -> None:
@ -63,6 +68,7 @@ class Database:
self.themes = _Themes()
self.categories = _Categories()
self.tags = _Tags()
self.users = _Users()
db = Database()

View file

@ -108,7 +108,10 @@ class BaseDocument:
db_entries = [x.dict() for x in result]
if limit == 1:
try:
return db_entries[0]
except IndexError:
return None
return db_entries
@ -124,9 +127,8 @@ class BaseDocument:
"""
new_document = self.sql_model(session=session, **document)
session.add(new_document)
return_data = new_document.dict()
session.commit()
return_data = new_document.dict()
return return_data
def update(self, session: Session, match_value: str, new_data: str) -> dict:

View file

@ -2,3 +2,4 @@ from db.sql.meal_models import *
from db.sql.recipe_models import *
from db.sql.settings_models import *
from db.sql.theme_models import *
from db.sql.users import *

44
mealie/db/sql/users.py Normal file
View file

@ -0,0 +1,44 @@
from db.sql.model_base import BaseMixins, SqlAlchemyBase
from sqlalchemy import Boolean, Column, Integer, String
class User(SqlAlchemyBase, BaseMixins):
__tablename__ = "users"
id = Column(Integer, primary_key=True)
full_name = Column(String, index=True)
email = Column(String, unique=True, index=True)
password = Column(String)
is_active = Column(Boolean(), default=True)
family = Column(String)
is_superuser = Column(Boolean(), default=False)
def __init__(
self,
session,
full_name,
email,
password,
family="public",
is_superuser=False,
) -> None:
self.full_name = full_name
self.email = email
self.family = family
self.is_superuser = is_superuser
self.password = password
def dict(self):
return {
"id": self.id,
"full_name": self.full_name,
"email": self.email,
"is_superuser": self.is_superuser,
"family": self.family,
"password": self.password,
}
def update(self, full_name, email, family, password, session=None):
self.full_name = full_name
self.email = email
self.family = family
self.password = password

View file

@ -0,0 +1,23 @@
from pydantic import BaseModel
class CreateUser(BaseModel):
full_name: str
email: str
password: str
family: str
class Config:
schema_extra = {
"full_name": "Change Me",
"email": "changeme@email.com",
"password": "MyPassword",
"family": "public",
}
class UserResponse(BaseModel):
id: int
full_name: str
email: str
family: str

View file

View file

@ -2,7 +2,6 @@ import json
from app_config import APP_VERSION, DEBUG_DIR
from fastapi import APIRouter
from fastapi.responses import HTMLResponse
from utils.logger import LOGGER_FILE
router = APIRouter(prefix="/api/debug", tags=["Debug"])

21
mealie/routes/deps.py Normal file
View file

@ -0,0 +1,21 @@
from app_config import SECRET
from db.database import db
from db.db_setup import create_session
from fastapi_login import LoginManager
from sqlalchemy.orm.session import Session
manager = LoginManager(SECRET, "/api/auth/token")
@manager.user_loader
def query_user(user_email: str, session: Session = None):
"""
Get a user from the db
:param user_id: E-Mail of the user
:return: None or the user object
"""
session = session if session else create_session()
user = db.users.get(session, user_email, "email")
session.close()
return user

View file

View file

@ -0,0 +1,26 @@
from db.db_setup import generate_session
from fastapi import APIRouter, Depends
from fastapi.security import OAuth2PasswordRequestForm
from fastapi_login.exceptions import InvalidCredentialsException
from routes.deps import manager, query_user
from sqlalchemy.orm.session import Session
router = APIRouter(prefix="/api/auth", tags=["Auth"])
@router.post("/token")
def token(
data: OAuth2PasswordRequestForm = Depends(),
session: Session = Depends(generate_session),
):
email = data.username
password = data.password
user = query_user(email, session)
if not user:
raise InvalidCredentialsException # you can also use your own HTTPException
elif password != user["password"]:
raise InvalidCredentialsException
access_token = manager.create_access_token(data=dict(sub=email))
return {"access_token": access_token, "token_type": "bearer"}

View file

@ -0,0 +1,64 @@
from db.database import db
from db.db_setup import generate_session
from fastapi import APIRouter, Depends
from models.user_models import CreateUser, UserResponse
from routes.deps import manager, query_user
from sqlalchemy.orm.session import Session
router = APIRouter(prefix="/api/users", tags=["Users"])
@router.post("/", response_model=UserResponse)
async def create_user(
new_user: CreateUser,
current_user=Depends(manager),
session: Session = Depends(generate_session),
):
""" Returns a list of all user in the Database """
data = db.users.create(session, new_user.dict())
print(data)
return data
@router.get("/", response_model=list[UserResponse])
async def get_all_users(
current_user=Depends(manager), session: Session = Depends(generate_session)
):
return db.users.get_all(session)
@router.get("/{id}", response_model=UserResponse)
async def get_user_by_id(
id: int, current_user=Depends(manager), session: Session = Depends(generate_session)
):
return db.users.get(session, id)
@router.put("/{id}", response_model=UserResponse)
async def update_user(
id: int,
new_data: CreateUser,
current_user=Depends(manager),
session: Session = Depends(generate_session),
):
current_user_id = current_user.get("id")
is_superuser = current_user.get("is_superuser")
if current_user_id == id or is_superuser:
return db.users.update(session, id, new_data.dict())
return
@router.delete("/{id}")
async def delete_user(
id: int,
current_user=Depends(manager),
session: Session = Depends(generate_session),
):
""" Removes a user from the database. Must be the current user or a super user"""
current_user_id = current_user.get("id")
is_superuser = current_user.get("is_superuser")
if current_user_id == id or is_superuser:
return db.users.delete(session, id)

View file

@ -0,0 +1,7 @@
from fastapi import APIRouter
from routes.users import auth, crud
router = APIRouter()
router.include_router(auth.router)
router.include_router(crud.router)

View file

View file

@ -0,0 +1,25 @@
from db.database import db
from db.db_setup import create_session, sql_exists
from fastapi.logger import logger
def init_super_user():
session = create_session()
default_user = {
"full_name": "Change Me",
"email": "changeme@email.com",
"password": "MyPassword",
"family": "public",
"is_superuser": True,
}
logger.info("Generating Default User")
db.users.create(session, default_user)
session.close()
if not sql_exists:
init_super_user()

86
poetry.lock generated
View file

@ -211,6 +211,19 @@ dev = ["python-jose[cryptography] (>=3.1.0,<4.0.0)", "passlib[bcrypt] (>=1.7.2,<
doc = ["mkdocs (>=1.1.2,<2.0.0)", "mkdocs-material (>=6.1.4,<7.0.0)", "markdown-include (>=0.5.1,<0.6.0)", "mkdocs-markdownextradata-plugin (>=0.1.7,<0.2.0)", "typer-cli (>=0.0.9,<0.0.10)", "pyyaml (>=5.3.1,<6.0.0)"]
test = ["pytest (==5.4.3)", "pytest-cov (==2.10.0)", "pytest-asyncio (>=0.14.0,<0.15.0)", "mypy (==0.790)", "flake8 (>=3.8.3,<4.0.0)", "black (==20.8b1)", "isort (>=5.0.6,<6.0.0)", "requests (>=2.24.0,<3.0.0)", "httpx (>=0.14.0,<0.15.0)", "email_validator (>=1.1.1,<2.0.0)", "sqlalchemy (>=1.3.18,<2.0.0)", "peewee (>=3.13.3,<4.0.0)", "databases[sqlite] (>=0.3.2,<0.4.0)", "orjson (>=3.2.1,<4.0.0)", "async_exit_stack (>=1.0.1,<2.0.0)", "async_generator (>=1.10,<2.0.0)", "python-multipart (>=0.0.5,<0.0.6)", "aiofiles (>=0.5.0,<0.6.0)", "flask (>=1.1.2,<2.0.0)"]
[[package]]
name = "fastapi-login"
version = "1.5.3"
description = ""
category = "main"
optional = false
python-versions = "*"
[package.dependencies]
fastapi = "*"
passlib = "*"
pyjwt = "*"
[[package]]
name = "h11"
version = "0.12.0"
@ -403,6 +416,20 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
[package.dependencies]
pyparsing = ">=2.0.2"
[[package]]
name = "passlib"
version = "1.7.4"
description = "comprehensive password hashing framework supporting over 30 schemes"
category = "main"
optional = false
python-versions = "*"
[package.extras]
argon2 = ["argon2-cffi (>=18.2.0)"]
bcrypt = ["bcrypt (>=3.1.0)"]
build_docs = ["sphinx (>=1.6)", "sphinxcontrib-fulltoc (>=1.2.0)", "cloud-sptheme (>=1.10.1)"]
totp = ["cryptography"]
[[package]]
name = "pathspec"
version = "0.8.1"
@ -443,6 +470,20 @@ dotenv = ["python-dotenv (>=0.10.4)"]
email = ["email-validator (>=1.0.3)"]
typing_extensions = ["typing-extensions (>=3.7.2)"]
[[package]]
name = "pyjwt"
version = "2.0.1"
description = "JSON Web Token implementation in Python"
category = "main"
optional = false
python-versions = ">=3.6"
[package.extras]
crypto = ["cryptography (>=3.3.1,<4.0.0)"]
dev = ["sphinx", "sphinx-rtd-theme", "zope.interface", "cryptography (>=3.3.1,<4.0.0)", "pytest (>=6.0.0,<7.0.0)", "coverage[toml] (==5.0.4)", "mypy", "pre-commit"]
docs = ["sphinx", "sphinx-rtd-theme", "zope.interface"]
tests = ["pytest (>=6.0.0,<7.0.0)", "coverage[toml] (==5.0.4)"]
[[package]]
name = "pylint"
version = "2.6.0"
@ -742,7 +783,7 @@ socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"]
[[package]]
name = "uvicorn"
version = "0.13.3"
version = "0.13.4"
description = "The lightning-fast ASGI server."
category = "main"
optional = false
@ -755,12 +796,12 @@ h11 = ">=0.8"
httptools = {version = ">=0.1.0,<0.2.0", optional = true, markers = "sys_platform != \"win32\" and sys_platform != \"cygwin\" and platform_python_implementation != \"PyPy\" and extra == \"standard\""}
python-dotenv = {version = ">=0.13", optional = true, markers = "extra == \"standard\""}
PyYAML = {version = ">=5.1", optional = true, markers = "extra == \"standard\""}
uvloop = {version = ">=0.14.0", optional = true, markers = "sys_platform != \"win32\" and sys_platform != \"cygwin\" and platform_python_implementation != \"PyPy\" and extra == \"standard\""}
watchgod = {version = ">=0.6,<0.7", optional = true, markers = "extra == \"standard\""}
uvloop = {version = ">=0.14.0,<0.15.0 || >0.15.0,<0.15.1 || >0.15.1", optional = true, markers = "sys_platform != \"win32\" and sys_platform != \"cygwin\" and platform_python_implementation != \"PyPy\" and extra == \"standard\""}
watchgod = {version = ">=0.6", optional = true, markers = "extra == \"standard\""}
websockets = {version = ">=8.0.0,<9.0.0", optional = true, markers = "extra == \"standard\""}
[package.extras]
standard = ["websockets (>=8.0.0,<9.0.0)", "watchgod (>=0.6,<0.7)", "python-dotenv (>=0.13)", "PyYAML (>=5.1)", "httptools (>=0.1.0,<0.2.0)", "uvloop (>=0.14.0)", "colorama (>=0.4)"]
standard = ["websockets (>=8.0.0,<9.0.0)", "watchgod (>=0.6)", "python-dotenv (>=0.13)", "PyYAML (>=5.1)", "httptools (>=0.1.0,<0.2.0)", "uvloop (>=0.14.0,!=0.15.0,!=0.15.1)", "colorama (>=0.4)"]
[[package]]
name = "uvloop"
@ -831,7 +872,7 @@ python-versions = "*"
[metadata]
lock-version = "1.1"
python-versions = "^3.8"
content-hash = "fbe2a3d2885fcc24abe5a285a961f968ea32aa22182f88f44a7c9dd624c968b5"
content-hash = "957b8a18406390a7bf3d02b0ccbf0af89e52d0a7d89e25adb81650f942d5108c"
[metadata.files]
aiofiles = [
@ -949,6 +990,10 @@ fastapi = [
{file = "fastapi-0.63.0-py3-none-any.whl", hash = "sha256:98d8ea9591d8512fdadf255d2a8fa56515cdd8624dca4af369da73727409508e"},
{file = "fastapi-0.63.0.tar.gz", hash = "sha256:63c4592f5ef3edf30afa9a44fa7c6b7ccb20e0d3f68cd9eba07b44d552058dcb"},
]
fastapi-login = [
{file = "fastapi-login-1.5.3.tar.gz", hash = "sha256:8e8ef710f1b7107e81d00e205779e73e17be35d5a91d11685ff72f323898e93b"},
{file = "fastapi_login-1.5.3-py3-none-any.whl", hash = "sha256:6c83b74bdb45c34ec0aab22000a7951df96c5d011f02a99a46ca4b2be6b1263c"},
]
h11 = [
{file = "h11-0.12.0-py3-none-any.whl", hash = "sha256:36a3cb8c0a032f56e2da7084577878a035d3b61d104230d4bd49c0c6b555a9c6"},
{file = "h11-0.12.0.tar.gz", hash = "sha256:47222cb6067e4a307d535814917cd98fd0a57b6788ce715755fa2b6c28b56042"},
@ -1083,20 +1128,39 @@ markupsafe = [
{file = "MarkupSafe-1.1.1-cp35-cp35m-win32.whl", hash = "sha256:6dd73240d2af64df90aa7c4e7481e23825ea70af4b4922f8ede5b9e35f78a3b1"},
{file = "MarkupSafe-1.1.1-cp35-cp35m-win_amd64.whl", hash = "sha256:9add70b36c5666a2ed02b43b335fe19002ee5235efd4b8a89bfcf9005bebac0d"},
{file = "MarkupSafe-1.1.1-cp36-cp36m-macosx_10_6_intel.whl", hash = "sha256:24982cc2533820871eba85ba648cd53d8623687ff11cbb805be4ff7b4c971aff"},
{file = "MarkupSafe-1.1.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:d53bc011414228441014aa71dbec320c66468c1030aae3a6e29778a3382d96e5"},
{file = "MarkupSafe-1.1.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:00bc623926325b26bb9605ae9eae8a215691f33cae5df11ca5424f06f2d1f473"},
{file = "MarkupSafe-1.1.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:717ba8fe3ae9cc0006d7c451f0bb265ee07739daf76355d06366154ee68d221e"},
{file = "MarkupSafe-1.1.1-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:3b8a6499709d29c2e2399569d96719a1b21dcd94410a586a18526b143ec8470f"},
{file = "MarkupSafe-1.1.1-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:84dee80c15f1b560d55bcfe6d47b27d070b4681c699c572af2e3c7cc90a3b8e0"},
{file = "MarkupSafe-1.1.1-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:b1dba4527182c95a0db8b6060cc98ac49b9e2f5e64320e2b56e47cb2831978c7"},
{file = "MarkupSafe-1.1.1-cp36-cp36m-win32.whl", hash = "sha256:535f6fc4d397c1563d08b88e485c3496cf5784e927af890fb3c3aac7f933ec66"},
{file = "MarkupSafe-1.1.1-cp36-cp36m-win_amd64.whl", hash = "sha256:b1282f8c00509d99fef04d8ba936b156d419be841854fe901d8ae224c59f0be5"},
{file = "MarkupSafe-1.1.1-cp37-cp37m-macosx_10_6_intel.whl", hash = "sha256:8defac2f2ccd6805ebf65f5eeb132adcf2ab57aa11fdf4c0dd5169a004710e7d"},
{file = "MarkupSafe-1.1.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:bf5aa3cbcfdf57fa2ee9cd1822c862ef23037f5c832ad09cfea57fa846dec193"},
{file = "MarkupSafe-1.1.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:46c99d2de99945ec5cb54f23c8cd5689f6d7177305ebff350a58ce5f8de1669e"},
{file = "MarkupSafe-1.1.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:ba59edeaa2fc6114428f1637ffff42da1e311e29382d81b339c1817d37ec93c6"},
{file = "MarkupSafe-1.1.1-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:6fffc775d90dcc9aed1b89219549b329a9250d918fd0b8fa8d93d154918422e1"},
{file = "MarkupSafe-1.1.1-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:a6a744282b7718a2a62d2ed9d993cad6f5f585605ad352c11de459f4108df0a1"},
{file = "MarkupSafe-1.1.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:195d7d2c4fbb0ee8139a6cf67194f3973a6b3042d742ebe0a9ed36d8b6f0c07f"},
{file = "MarkupSafe-1.1.1-cp37-cp37m-win32.whl", hash = "sha256:b00c1de48212e4cc9603895652c5c410df699856a2853135b3967591e4beebc2"},
{file = "MarkupSafe-1.1.1-cp37-cp37m-win_amd64.whl", hash = "sha256:9bf40443012702a1d2070043cb6291650a0841ece432556f784f004937f0f32c"},
{file = "MarkupSafe-1.1.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6788b695d50a51edb699cb55e35487e430fa21f1ed838122d722e0ff0ac5ba15"},
{file = "MarkupSafe-1.1.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:cdb132fc825c38e1aeec2c8aa9338310d29d337bebbd7baa06889d09a60a1fa2"},
{file = "MarkupSafe-1.1.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:13d3144e1e340870b25e7b10b98d779608c02016d5184cfb9927a9f10c689f42"},
{file = "MarkupSafe-1.1.1-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:acf08ac40292838b3cbbb06cfe9b2cb9ec78fce8baca31ddb87aaac2e2dc3bc2"},
{file = "MarkupSafe-1.1.1-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:d9be0ba6c527163cbed5e0857c451fcd092ce83947944d6c14bc95441203f032"},
{file = "MarkupSafe-1.1.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:caabedc8323f1e93231b52fc32bdcde6db817623d33e100708d9a68e1f53b26b"},
{file = "MarkupSafe-1.1.1-cp38-cp38-win32.whl", hash = "sha256:596510de112c685489095da617b5bcbbac7dd6384aeebeda4df6025d0256a81b"},
{file = "MarkupSafe-1.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:e8313f01ba26fbbe36c7be1966a7b7424942f670f38e666995b88d012765b9be"},
{file = "MarkupSafe-1.1.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d73a845f227b0bfe8a7455ee623525ee656a9e2e749e4742706d80a6065d5e2c"},
{file = "MarkupSafe-1.1.1-cp39-cp39-manylinux1_i686.whl", hash = "sha256:98bae9582248d6cf62321dcb52aaf5d9adf0bad3b40582925ef7c7f0ed85fceb"},
{file = "MarkupSafe-1.1.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:2beec1e0de6924ea551859edb9e7679da6e4870d32cb766240ce17e0a0ba2014"},
{file = "MarkupSafe-1.1.1-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:7fed13866cf14bba33e7176717346713881f56d9d2bcebab207f7a036f41b850"},
{file = "MarkupSafe-1.1.1-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:6f1e273a344928347c1290119b493a1f0303c52f5a5eae5f16d74f48c15d4a85"},
{file = "MarkupSafe-1.1.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:feb7b34d6325451ef96bc0e36e1a6c0c1c64bc1fbec4b854f4529e51887b1621"},
{file = "MarkupSafe-1.1.1-cp39-cp39-win32.whl", hash = "sha256:22c178a091fc6630d0d045bdb5992d2dfe14e3259760e713c490da5323866c39"},
{file = "MarkupSafe-1.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:b7d644ddb4dbd407d31ffb699f1d140bc35478da613b441c582aeb7c43838dd8"},
{file = "MarkupSafe-1.1.1.tar.gz", hash = "sha256:29872e92839765e546828bb7754a68c418d927cd064fd4708fab9fe9c8bb116b"},
]
mccabe = [
@ -1114,6 +1178,10 @@ packaging = [
{file = "packaging-20.8-py2.py3-none-any.whl", hash = "sha256:24e0da08660a87484d1602c30bb4902d74816b6985b93de36926f5bc95741858"},
{file = "packaging-20.8.tar.gz", hash = "sha256:78598185a7008a470d64526a8059de9aaa449238f280fc9eb6b13ba6c4109093"},
]
passlib = [
{file = "passlib-1.7.4-py2.py3-none-any.whl", hash = "sha256:aa6bca462b8d8bda89c70b382f0c298a20b5560af6cbfa2dce410c0a2fb669f1"},
{file = "passlib-1.7.4.tar.gz", hash = "sha256:defd50f72b65c5402ab2c573830a6978e5f202ad0d984793c8dde2c4152ebe04"},
]
pathspec = [
{file = "pathspec-0.8.1-py2.py3-none-any.whl", hash = "sha256:aa0cb481c4041bf52ffa7b0d8fa6cd3e88a2ca4879c533c9153882ee2556790d"},
{file = "pathspec-0.8.1.tar.gz", hash = "sha256:86379d6b86d75816baba717e64b1a3a3469deb93bb76d613c9ce79edc5cb68fd"},
@ -1150,6 +1218,10 @@ pydantic = [
{file = "pydantic-1.7.3-py3-none-any.whl", hash = "sha256:38be427ea01a78206bcaf9a56f835784afcba9e5b88fbdce33bbbfbcd7841229"},
{file = "pydantic-1.7.3.tar.gz", hash = "sha256:213125b7e9e64713d16d988d10997dabc6a1f73f3991e1ff8e35ebb1409c7dc9"},
]
pyjwt = [
{file = "PyJWT-2.0.1-py3-none-any.whl", hash = "sha256:b70b15f89dc69b993d8a8d32c299032d5355c82f9b5b7e851d1a6d706dffe847"},
{file = "PyJWT-2.0.1.tar.gz", hash = "sha256:a5c70a06e1f33d81ef25eecd50d50bd30e34de1ca8b2b9fa3fe0daaabcf69bf7"},
]
pylint = [
{file = "pylint-2.6.0-py3-none-any.whl", hash = "sha256:bfe68f020f8a0fece830a22dd4d5dddb4ecc6137db04face4c3420a46a52239f"},
{file = "pylint-2.6.0.tar.gz", hash = "sha256:bb4a908c9dadbc3aac18860550e870f58e1a02c9f2c204fdf5693d73be061210"},
@ -1371,8 +1443,8 @@ urllib3 = [
{file = "urllib3-1.26.2.tar.gz", hash = "sha256:19188f96923873c92ccb987120ec4acaa12f0461fa9ce5d3d0772bc965a39e08"},
]
uvicorn = [
{file = "uvicorn-0.13.3-py3-none-any.whl", hash = "sha256:1079c50a06f6338095b4f203e7861dbff318dde5f22f3a324fc6e94c7654164c"},
{file = "uvicorn-0.13.3.tar.gz", hash = "sha256:ef1e0bb5f7941c6fe324e06443ddac0331e1632a776175f87891c7bd02694355"},
{file = "uvicorn-0.13.4-py3-none-any.whl", hash = "sha256:7587f7b08bd1efd2b9bad809a3d333e972f1d11af8a5e52a9371ee3a5de71524"},
{file = "uvicorn-0.13.4.tar.gz", hash = "sha256:3292251b3c7978e8e4a7868f4baf7f7f7bb7e40c759ecc125c37e99cdea34202"},
]
uvloop = [
{file = "uvloop-0.14.0-cp35-cp35m-macosx_10_11_x86_64.whl", hash = "sha256:08b109f0213af392150e2fe6f81d33261bb5ce968a288eb698aad4f46eb711bd"},

View file

@ -25,6 +25,7 @@ PyYAML = "^5.3.1"
extruct = "^0.12.0"
scrape-schema-recipe = "^0.1.3"
python-multipart = "^0.0.5"
fastapi-login = "^1.5.3"
[tool.poetry.dev-dependencies]
pylint = "^2.6.0"