mirror of
https://github.com/hay-kot/mealie.git
synced 2025-08-22 06:23:34 -07:00
basic crud NOT SECURE
This commit is contained in:
parent
d986190a55
commit
838ca2a6f8
18 changed files with 311 additions and 14 deletions
|
@ -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()
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -108,7 +108,10 @@ class BaseDocument:
|
|||
db_entries = [x.dict() for x in result]
|
||||
|
||||
if limit == 1:
|
||||
return db_entries[0]
|
||||
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:
|
||||
|
|
|
@ -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
44
mealie/db/sql/users.py
Normal 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
|
23
mealie/models/user_models.py
Normal file
23
mealie/models/user_models.py
Normal 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
|
0
mealie/routes/__init__.py
Normal file
0
mealie/routes/__init__.py
Normal 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
21
mealie/routes/deps.py
Normal 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
|
0
mealie/routes/users/__init__.py
Normal file
0
mealie/routes/users/__init__.py
Normal file
26
mealie/routes/users/auth.py
Normal file
26
mealie/routes/users/auth.py
Normal 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"}
|
64
mealie/routes/users/crud.py
Normal file
64
mealie/routes/users/crud.py
Normal 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)
|
7
mealie/routes/users/users.py
Normal file
7
mealie/routes/users/users.py
Normal 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)
|
0
mealie/services/users/__init__.py
Normal file
0
mealie/services/users/__init__.py
Normal file
25
mealie/services/users/users.py
Normal file
25
mealie/services/users/users.py
Normal 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
86
poetry.lock
generated
|
@ -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"},
|
||||
|
|
|
@ -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"
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue