Skip to content

Commit

Permalink
Merge pull request #4 from aSel1x/main
Browse files Browse the repository at this point in the history
  • Loading branch information
ProFastCode authored Jul 25, 2024
2 parents 4f24433 + 65ca4c4 commit 5da21a9
Show file tree
Hide file tree
Showing 31 changed files with 202 additions and 163 deletions.
31 changes: 20 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@
│   ├── deps.py
│   └── v1
│   ├── __init__.py
│   ├── auth
│   │   ├── __init__.py
│   │   └── token.py
│   └── users
│   ├── __init__.py
│   ├── auth
│   │   ├── __init__.py
│   │   └── token.py
│   ├── create.py
│   └── retrieve.py
├── core
Expand All @@ -23,27 +23,36 @@
│   └── settings.py
├── logic
│   ├── __init__.py
│   ├── auth
│   │   ├── __init__.py
│   │   └── auth.py
│   ├── logic.py
│   ├── security
│   │   ├── __init__.py
│   │   ├── jwt.py
│   │   └── pwd.py
│   │   ├── pwd.py
│   │   └── security.py
│   └── users
│   ├── __init__.py
│   ├── auth
│   │   ├── __init__.py
│   │   └── auth.py
│   └── users.py
├── models
│   ├── __init__.py
│   ├── auth
│   │   ├── __init__.py
│   │   └── token.py
│   ├── base.py
│   ├── token.py
│   └── user.py
│   ├── types
│   │   ├── __init__.py
│   │   └── unix.py
│   └── users
│   ├── __init__.py
│   └── user.py
└── repositories
├── __init__.py
├── abstract.py
├── base.py
└── user.py
11 directories, 28 files
14 directories, 34 files
```

## Create a `.env` file based on `.env.dist` and make all the necessary customizations
Expand Down
9 changes: 5 additions & 4 deletions app/api/deps.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,20 @@
from fastapi.security import APIKeyHeader

from app.logic import Logic as _Logic
from app.models.user import User as _User
from app.models.users.user import User as _User


async def get_logic() -> _Logic:
return await _Logic.create()
async with Logic.create() as logic:
yield logic


Logic = Annotated[_Logic, Depends(get_logic)]


async def get_user(
token: Annotated[str, Depends(APIKeyHeader(name='access-token'))],
logic: Logic,
token: Annotated[str, Depends(APIKeyHeader(name='access-token'))],
logic: Logic,
) -> _User | None:
return await logic.users.retrieve_by_token(token)

Expand Down
3 changes: 2 additions & 1 deletion app/api/v1/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,12 @@

from fastapi import APIRouter

from . import users
from . import auth, users

FOLDER_NAME = f'{Path(__file__).parent.name}'

router = APIRouter(prefix=f'/{FOLDER_NAME}', tags=[FOLDER_NAME])
router.include_router(auth.router)
router.include_router(users.router)

__all__ = ['router']
File renamed without changes.
6 changes: 3 additions & 3 deletions app/api/v1/users/auth/token.py → app/api/v1/auth/token.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
from fastapi import APIRouter

from app.api import deps
from app.models.token import AccessToken
from app.models.user import UserCreate
from app.models.auth import AccessToken
from app.models.users.user import UserCreate

router = APIRouter(prefix='/token')

Expand All @@ -12,7 +12,7 @@ async def token(data: UserCreate, logic: deps.Logic):
"""
Retrieve new access token
"""
return await logic.users.auth.generate_token(**data.model_dump())
return await logic.auth.generate_token(**data.model_dump())


__all__ = ['router']
3 changes: 1 addition & 2 deletions app/api/v1/users/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,9 @@

from fastapi import APIRouter

from . import auth, create, retrieve
from . import create, retrieve

router = APIRouter(prefix='/users', tags=['users'])
router.include_router(auth.router)
router.include_router(create.router)
router.include_router(retrieve.router)

Expand Down
4 changes: 2 additions & 2 deletions app/api/v1/users/create.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from fastapi import APIRouter

from app.api import deps
from app.models.user import UserCreate, UserRead
from app.models.users.user import UserCreate, UserRead

router = APIRouter(prefix='/create')

Expand All @@ -11,7 +11,7 @@ async def create(data: UserCreate, logic: deps.Logic):
"""
Create user
"""
return await logic.users.create(**data.model_dump())
return await logic.users.create(data)


__all__ = ['router']
2 changes: 1 addition & 1 deletion app/api/v1/users/retrieve.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from fastapi import APIRouter

from app.api import deps
from app.models.user import UserRead
from app.models.users.user import UserRead

router = APIRouter()

Expand Down
42 changes: 22 additions & 20 deletions app/core/db.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
Database
"""

from typing import NoReturn, Self
from typing import Self

from sqlalchemy.ext.asyncio import (AsyncEngine, async_sessionmaker,
create_async_engine)
Expand All @@ -15,47 +15,49 @@
class Database:
_instance = None

def __new__(cls, *args, **kwargs) -> Self:
def __new__(cls, *args, **kwargs) -> 'Database':
if cls._instance is None:
cls._instance = super(Database, cls).__new__(cls)
return cls._instance

def __init__(
self,
engine: AsyncEngine | None = None,
session: AsyncSession | None = None,
self,
engine: AsyncEngine | None = None,
session: AsyncSession | None = None,
) -> None:
if not hasattr(self, 'initialized'):
self.engine = engine
self.session = session
self.__engine = engine
self.__session = session
self.initialized = True

async def __set_async_engine(self) -> NoReturn:
if self.engine is None:
self.engine = create_async_engine(
async def __set_async_engine(self) -> None:
if self.__engine is None:
self.__engine = create_async_engine(
settings.pg_dsn.unicode_string(), echo=False, future=True
)

async def __set_async_session(self) -> NoReturn:
if self.session is None:
self.session = async_sessionmaker(
async def __set_async_session(self) -> None:
if self.__session is None:
self.__session = async_sessionmaker(
autocommit=False,
autoflush=False,
bind=self.engine,
bind=self.__engine,
class_=AsyncSession,
expire_on_commit=False,
)()

async def __set_repositories(self) -> NoReturn:
if self.session is not None:
self.user = repos.UserRepo(session=self.session)
async def __set_repositories(self) -> None:
if self.__session is not None:
self.user = repos.UserRepo(session=self.__session)

async def __aenter__(self) -> Self:
await self.__set_async_engine()
await self.__set_async_session()
await self.__set_repositories()
return self

async def __aexit__(self, exc_type, exc_value, traceback) -> NoReturn:
if self.session is not None:
await self.session.close()
async def __aexit__(self, exc_type, exc_value, traceback) -> None:
if self.__session is not None:
await self.__session.commit()
await self.__session.close()
self.__session = None
20 changes: 1 addition & 19 deletions app/logic/__init__.py
Original file line number Diff line number Diff line change
@@ -1,21 +1,3 @@
from typing import Self

from app.core.db import Database

from .security import Security
from .users import Users


class Logic:
def __init__(self, db: Database):
self.db = db
self.security = Security()
self.users = Users(self)

@classmethod
async def create(cls) -> Self:
async with Database() as db:
return cls(db)

from .logic import Logic

__all__ = ['Logic']
File renamed without changes.
24 changes: 24 additions & 0 deletions app/logic/auth/auth.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
from typing import TYPE_CHECKING

from app.core import exps
from app.models.auth import AccessToken

if TYPE_CHECKING:
from app.logic import Logic


class Auth:
def __init__(self, logic: 'Logic'):
self.logic = logic

async def generate_token(
self, email: str, password: str
) -> AccessToken | None:
if (user := await self.logic.db.user.retrieve_by_email(email)) is None:
raise exps.UserNotFoundException()
if not self.logic.security.pwd.checkpwd(password, user.password):
raise exps.UserIsCorrectException()
access_token = self.logic.security.jwt.encode_token(
{'id': user.id}, 1440
)
return AccessToken(token=access_token)
22 changes: 22 additions & 0 deletions app/logic/logic.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
from typing import Self, AsyncGenerator
from contextlib import asynccontextmanager

from app.core.db import Database

from .security import Security
from .users import Users
from .auth import Auth


class Logic:
def __init__(self, db: Database):
self.db = db
self.security = Security()
self.users = Users(self)
self.auth = Auth(self)

@classmethod
@asynccontextmanager
async def create(cls) -> AsyncGenerator[Self, None]:
async with Database() as db:
yield cls(db)
12 changes: 1 addition & 11 deletions app/logic/security/__init__.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,3 @@
from app.core.settings import settings

from .jwt import JWT
from .pwd import PWD


class Security:
def __init__(self):
self.jwt = JWT(settings.APP_SECRET_KEY)
self.pwd = PWD()

from .security import Security

__all__ = ['Security']
6 changes: 4 additions & 2 deletions app/logic/security/jwt.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ class JWT:
def __init__(self, secret_key: str):
self.secret_key: str = secret_key

def decode_token(self, token: str) -> dict | None:
def decode_token(self, token: str) -> dict:
try:
payload = jwt.decode(token, self.secret_key, algorithms=['HS256'])
except Exception:
Expand All @@ -18,7 +18,9 @@ def decode_token(self, token: str) -> dict | None:
exp = payload.get('exp')
if exp and dt.datetime.now(dt.UTC).timestamp() > exp:
raise exps.TokenExpiredException()
return payload.get('payload')
if (payload := payload.get('payload', None)) is None:
raise exps.TokenInvalidException()
return payload

def encode_token(self, payload: dict, minutes: int) -> str:
claims = {
Expand Down
6 changes: 4 additions & 2 deletions app/logic/security/pwd.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@


class PWD:
def hashpwd(self, password: str) -> str:
@staticmethod
def hashpwd(password: str) -> str:
return bcrypt.hashpw(password.encode(), bcrypt.gensalt()).decode()

def checkpwd(self, password: str, hashed_password: str) -> bool:
@staticmethod
def checkpwd(password: str, hashed_password: str) -> bool:
return bcrypt.checkpw(password.encode(), hashed_password.encode())
10 changes: 10 additions & 0 deletions app/logic/security/security.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
from app.core.settings import settings

from .jwt import JWT
from .pwd import PWD


class Security:
def __init__(self):
self.jwt = JWT(settings.APP_SECRET_KEY)
self.pwd = PWD()
24 changes: 0 additions & 24 deletions app/logic/users/auth/auth.py

This file was deleted.

Loading

0 comments on commit 5da21a9

Please sign in to comment.