-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add and delete files, update dependencies, and refactor code
- Loading branch information
Showing
31 changed files
with
1,457 additions
and
190 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
from datetime import datetime, timedelta | ||
from typing import Any, Union | ||
|
||
from jose import jwt | ||
from passlib.context import CryptContext | ||
|
||
from bemore.core import config | ||
|
||
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto") | ||
|
||
|
||
ALGORITHM = "HS256" | ||
|
||
|
||
def create_access_token( | ||
subject: Union[str, Any], expires_delta: timedelta = None | ||
) -> str: | ||
if expires_delta: | ||
expire = datetime.utcnow() + expires_delta | ||
else: | ||
expire = datetime.utcnow() + timedelta( | ||
minutes=config.ACCESS_TOKEN_EXPIRE_MINUTES | ||
) | ||
to_encode = {"exp": expire, "sub": str(subject)} | ||
encoded_jwt = jwt.encode(to_encode, config.SECRET_KEY, algorithm=ALGORITHM) | ||
return encoded_jwt | ||
|
||
|
||
def verify_password(plain_password: str, hashed_password: str) -> bool: | ||
return pwd_context.verify(plain_password, hashed_password) | ||
|
||
|
||
def get_password_hash(password: str) -> str: | ||
return pwd_context.hash(password) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
from typing import Any, Dict, Generic, Optional, Type, TypeVar, Union | ||
|
||
from fastapi.encoders import jsonable_encoder | ||
from pydantic import BaseModel | ||
from sqlalchemy.orm import Session | ||
|
||
ModelType = TypeVar("ModelType", bound=Any) | ||
CreateSchemaType = TypeVar("CreateSchemaType", bound=BaseModel) | ||
UpdateSchemaType = TypeVar("UpdateSchemaType", bound=BaseModel) | ||
|
||
|
||
class CRUDBase(Generic[ModelType, CreateSchemaType, UpdateSchemaType]): | ||
def __init__(self, model: Type[ModelType]): | ||
""" | ||
CRUD object with default methods to Create, Read, Update, Delete (CRUD). | ||
**Parameters** | ||
* `model`: A SQLAlchemy model class | ||
* `schema`: A Pydantic model (schema) class | ||
""" | ||
self.model = model | ||
|
||
def get(self, db: Session, id: Any) -> Optional[ModelType]: | ||
return db.query(self.model).filter(self.model.id == id).first() | ||
|
||
def create(self, db: Session, *, obj_in: CreateSchemaType) -> ModelType: | ||
obj_in_data = jsonable_encoder(obj_in) | ||
db_obj = self.model(**obj_in_data) # type: ignore | ||
db.add(db_obj) | ||
db.commit() | ||
db.refresh(db_obj) | ||
return db_obj | ||
|
||
def update( | ||
self, | ||
db: Session, | ||
*, | ||
db_obj: ModelType, | ||
obj_in: Union[UpdateSchemaType, Dict[str, Any]] | ||
) -> ModelType: | ||
obj_data = jsonable_encoder(db_obj) | ||
if isinstance(obj_in, dict): | ||
update_data = obj_in | ||
else: | ||
update_data = obj_in.dict(exclude_unset=True) | ||
for field in obj_data: | ||
if field in update_data: | ||
setattr(db_obj, field, update_data[field]) | ||
db.add(db_obj) | ||
db.commit() | ||
db.refresh(db_obj) | ||
return db_obj | ||
|
||
def remove(self, db: Session, *, id: int) -> ModelType: | ||
obj = db.query(self.model).get(id) | ||
db.delete(obj) | ||
db.commit() | ||
return obj |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
from typing import List | ||
|
||
from fastapi.encoders import jsonable_encoder | ||
from sqlalchemy.orm import Session | ||
|
||
from bemore.crud.base import CRUDBase | ||
from bemore.models import Item | ||
from bemore.schemas.item import ItemCreate, ItemUpdate | ||
|
||
|
||
class CRUDItem(CRUDBase[Item, ItemCreate, ItemUpdate]): | ||
def create_with_owner( | ||
self, db: Session, *, obj_in: ItemCreate, owner_id: int | ||
) -> Item: | ||
obj_in_data = jsonable_encoder(obj_in) | ||
db_obj = self.model(**obj_in_data, owner_id=owner_id) | ||
db.add(db_obj) | ||
db.commit() | ||
db.refresh(db_obj) | ||
return db_obj | ||
|
||
def get_multi_by_owner( | ||
self, db: Session, *, owner_id: int, skip: int = 0, limit: int = 100 | ||
) -> List[Item]: | ||
return ( | ||
db.query(self.model) | ||
.filter(Item.owner_id == owner_id) | ||
.offset(skip) | ||
.limit(limit) | ||
.all() | ||
) | ||
|
||
|
||
item = CRUDItem(Item) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
from typing import Any, Dict, Optional, Union | ||
|
||
from sqlalchemy.orm import Session | ||
|
||
from bemore.core.security import get_password_hash, verify_password | ||
from bemore.crud.base import CRUDBase | ||
from bemore.models import User | ||
from bemore.schemas.user import UserCreate, UserUpdate | ||
|
||
|
||
class CRUDUser(CRUDBase[User, UserCreate, UserUpdate]): | ||
def get_by_email(self, db: Session, *, email: str) -> Optional[User]: | ||
return db.query(User).filter(User.email == email).first() | ||
|
||
def create(self, db: Session, *, obj_in: UserCreate) -> User: | ||
db_obj = User( | ||
email=obj_in.email, | ||
hashed_password=get_password_hash(obj_in.password), | ||
full_name=obj_in.full_name, | ||
is_superuser=obj_in.is_superuser, | ||
) | ||
db.add(db_obj) | ||
db.commit() | ||
db.refresh(db_obj) | ||
return db_obj | ||
|
||
def update( | ||
self, db: Session, *, db_obj: User, obj_in: Union[UserUpdate, Dict[str, Any]] | ||
) -> User: | ||
if isinstance(obj_in, dict): | ||
update_data = obj_in | ||
else: | ||
update_data = obj_in.model_dump(exclude_unset=True) | ||
if update_data["password"]: | ||
hashed_password = get_password_hash(update_data["password"]) | ||
del update_data["password"] | ||
update_data["hashed_password"] = hashed_password | ||
return super().update(db, db_obj=db_obj, obj_in=update_data) | ||
|
||
def authenticate(self, db: Session, *, email: str, password: str) -> Optional[User]: | ||
user = self.get_by_email(db, email=email) | ||
if not user: | ||
return None | ||
if not verify_password(password, user.hashed_password): | ||
return None | ||
return user | ||
|
||
def is_active(self, user: User) -> bool: | ||
return user.is_active | ||
|
||
def is_superuser(self, user: User) -> bool: | ||
return user.is_superuser | ||
|
||
|
||
user = CRUDUser(User) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
from sqlmodel import create_engine | ||
|
||
from bemore.core.config import settings | ||
|
||
engine = create_engine(settings.SQLALCHEMY_DATABASE_URI) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
from sqlmodel import Session, select | ||
|
||
from bemore.crud.crud_user import user as crud | ||
from bemore.core.config import settings | ||
from bemore.models import User, UserCreate # noqa: F401 | ||
|
||
# make sure all SQLModel models are imported (app.models) before initializing DB | ||
# otherwise, SQLModel might fail to initialize relationships properly | ||
# for more details: https://github.com/tiangolo/full-stack-fastapi-postgresql/issues/28 | ||
|
||
|
||
def init_db(session: Session) -> None: | ||
# Tables should be created with Alembic migrations | ||
# But if you don't want to use migrations, create | ||
# the tables un-commenting the next line | ||
# Base.metadata.create_all(bind=engine) | ||
user = session.exec( | ||
select(User).where(User.email == settings.FIRST_SUPERUSER) | ||
).first() | ||
if not user: | ||
user_in = UserCreate( | ||
email=settings.FIRST_SUPERUSER, | ||
password=settings.FIRST_SUPERUSER_PASSWORD, | ||
is_superuser=True, | ||
) | ||
user = crud.create(db=session, obj_in=user_in) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
from sqlmodel import Session | ||
|
||
from bemore.db.engine import engine | ||
from bemore.db.init_db import init_db | ||
|
||
|
||
def init() -> None: | ||
with Session(engine) as session: | ||
init_db(session) | ||
|
||
|
||
def main() -> None: | ||
init() | ||
|
||
|
||
if __name__ == "__main__": | ||
main() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.