Skip to content

Commit 77902bc

Browse files
committed
0.0.1
1 parent 39c63ce commit 77902bc

35 files changed

+467
-1835
lines changed

app/__init__.py

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
1-
"""
2-
Init App Module
3-
"""
1+
import os
42

53
from fastapi import FastAPI
64
from fastapi.responses import JSONResponse
@@ -9,14 +7,16 @@
97
from app.core import exps
108
from app.core.settings import settings
119

10+
os.environ["TZ"] = "UTC"
11+
1212
app = FastAPI(
13-
title=settings.APP_TITLE,
14-
root_path=settings.APP_PATH,
15-
version=settings.APP_VERSION,
13+
title=settings.app_title,
14+
root_path=settings.app_path,
15+
version=settings.app_version,
1616
contact={
17-
'name': 'Fast Code',
18-
'url': 'https://fast-code.pro/',
19-
'email': 'fast.code.auth@gmail.com',
17+
"name": "Fast Code",
18+
"url": "https://fast-code.pro/",
19+
"email": "fast.code.auth@gmail.com",
2020
},
2121
)
2222

@@ -27,5 +27,5 @@
2727
async def exception_handler(request, exc: exps.CustomException):
2828
return JSONResponse(
2929
status_code=exc.status_code,
30-
content={'detail': exc.message},
30+
content={"detail": exc.message},
3131
)

app/api/deps.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,13 @@
55
from typing import Annotated, AsyncGenerator
66

77
from fastapi import Depends
8-
from fastapi.security import APIKeyHeader
8+
from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
99

1010
from app.logic import Logic as _Logic
11-
from app.models.users import User as _User
11+
from app.models.user import User as _User
12+
13+
14+
security = HTTPBearer()
1215

1316

1417
async def get_logic() -> AsyncGenerator[_Logic, None]:
@@ -20,10 +23,10 @@ async def get_logic() -> AsyncGenerator[_Logic, None]:
2023

2124

2225
async def get_user(
23-
token: Annotated[str, Depends(APIKeyHeader(name='access-token'))],
26+
creds: Annotated[HTTPAuthorizationCredentials, Depends(security)],
2427
logic: Logic,
2528
) -> _User | None:
26-
return await logic.users.retrieve_by_token(token)
29+
return await logic.user.retrieve_by_token(creds.credentials)
2730

2831

2932
User = Annotated[_User, Depends(get_user)]

app/api/v1/__init__.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,12 @@
66

77
from fastapi import APIRouter
88

9-
from . import auth, users
9+
from . import auth, user
1010

11-
FOLDER_NAME = f'{Path(__file__).parent.name}'
11+
FOLDER_NAME = f"{Path(__file__).parent.name}"
1212

13-
router = APIRouter(prefix=f'/{FOLDER_NAME}', tags=[FOLDER_NAME])
13+
router = APIRouter(prefix=f"/{FOLDER_NAME}", tags=[FOLDER_NAME])
1414
router.include_router(auth.router)
15-
router.include_router(users.router)
15+
router.include_router(user.router)
1616

17-
__all__ = ['router']
17+
__all__ = ["router"]

app/api/v1/auth.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
from fastapi import APIRouter
2+
3+
from app.api import deps
4+
from app.models import auth as auth_models
5+
from app.models import user as user_models
6+
7+
router = APIRouter(prefix="/auth")
8+
9+
10+
@router.post("/token", response_model=auth_models.AccessToken)
11+
async def token(data: user_models.UserCreate, logic: deps.Logic):
12+
"""
13+
Retrieve new access token
14+
"""
15+
return await logic.auth.generate_token(data)

app/api/v1/auth/__init__.py

Lines changed: 0 additions & 8 deletions
This file was deleted.

app/api/v1/auth/token.py

Lines changed: 0 additions & 18 deletions
This file was deleted.

app/api/v1/user.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
from fastapi import APIRouter
2+
3+
from app.api import deps
4+
from app.models import user as user_models
5+
6+
router = APIRouter(prefix="/users")
7+
8+
9+
@router.post("", response_model=user_models.UserRetrieve)
10+
async def create(data: user_models.UserCreate, logic: deps.Logic):
11+
"""
12+
Create user
13+
"""
14+
return await logic.user.create(data)
15+
16+
17+
@router.get("", response_model=user_models.UserRetrieve)
18+
async def retrieve(user: deps.User):
19+
"""
20+
Retrieve user
21+
"""
22+
return user

app/api/v1/users/__init__.py

Lines changed: 0 additions & 13 deletions
This file was deleted.

app/api/v1/users/create.py

Lines changed: 0 additions & 17 deletions
This file was deleted.

app/api/v1/users/retrieve.py

Lines changed: 0 additions & 21 deletions
This file was deleted.

app/core/__init__.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +0,0 @@
1-
"""
2-
Core App Module
3-
"""

app/core/db.py

Lines changed: 25 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,53 +1,56 @@
1-
"""
2-
Database
3-
"""
4-
51
from typing import Self
62

7-
from sqlalchemy.ext.asyncio import (AsyncEngine, async_sessionmaker,
8-
create_async_engine)
3+
from sqlalchemy.ext.asyncio import AsyncEngine, async_sessionmaker, create_async_engine
94
from sqlmodel.ext.asyncio.session import AsyncSession
105

116
from app import repositories as repos
127
from app.core.settings import settings
138

149

1510
class Database:
11+
user: repos.UserRepo
12+
13+
_instance = None
14+
15+
def __new__(cls, *args, **kwargs):
16+
if cls._instance is None:
17+
cls._instance = super().__new__(cls)
18+
return cls._instance
19+
1620
def __init__(
1721
self,
1822
engine: AsyncEngine | None = None,
19-
session: AsyncSession | None = None,
23+
session_maker: async_sessionmaker | None = None,
2024
) -> None:
21-
self.__engine = engine
22-
self.__session = session
25+
if not hasattr(self, "initialized"):
26+
self.__engine = engine
27+
self.__session_maker = session_maker
28+
self.__initialized = True
2329

2430
async def __set_async_engine(self) -> None:
2531
if self.__engine is None:
26-
self.__engine = create_async_engine(
27-
settings.db_dsn, echo=False, future=True
28-
)
32+
self.__engine = create_async_engine(settings.postgres_dsn)
2933

30-
async def __set_async_session(self) -> None:
31-
if self.__session is None:
32-
self.__session = async_sessionmaker(
34+
async def __set_session_maker(self) -> None:
35+
if self.__session_maker is None:
36+
self.__session_maker = async_sessionmaker(
3337
autocommit=False,
3438
autoflush=False,
3539
bind=self.__engine,
3640
class_=AsyncSession,
3741
expire_on_commit=False,
38-
)()
42+
)
3943

4044
async def __set_repositories(self) -> None:
41-
if self.__session is not None:
42-
self.user = repos.UserRepo(session=self.__session)
45+
if self.__session_maker is not None:
46+
self.user = repos.UserRepo(self.__session_maker)
4347

4448
async def __aenter__(self) -> Self:
4549
await self.__set_async_engine()
46-
await self.__set_async_session()
50+
await self.__set_session_maker()
4751
await self.__set_repositories()
4852
return self
4953

5054
async def __aexit__(self, exc_type, exc_value, traceback) -> None:
51-
if self.__session is not None:
52-
await self.__session.commit()
53-
await self.__session.close()
55+
if self.__engine is not None:
56+
await self.__engine.dispose()

app/core/exps.py

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,3 @@
1-
"""
2-
Exceptions
3-
"""
4-
5-
61
class CustomException(Exception):
72
def __init__(self, message: str, status_code: int = 500):
83
super().__init__(message)
@@ -13,25 +8,25 @@ def __init__(self, message: str, status_code: int = 500):
138
# Users
149
class UserExistsException(CustomException):
1510
def __init__(self):
16-
super().__init__('User is already taken.', status_code=409)
11+
super().__init__("User is already taken.", status_code=409)
1712

1813

1914
class UserNotFoundException(CustomException):
2015
def __init__(self):
21-
super().__init__('User not found.', status_code=404)
16+
super().__init__("User not found.", status_code=404)
2217

2318

2419
class UserIsCorrectException(CustomException):
2520
def __init__(self):
26-
super().__init__('User is correct.', status_code=401)
21+
super().__init__("User is correct.", status_code=401)
2722

2823

2924
# Tokens
3025
class TokenInvalidException(CustomException):
3126
def __init__(self):
32-
super().__init__('Invalid token.', status_code=401)
27+
super().__init__("Invalid token.", status_code=401)
3328

3429

3530
class TokenExpiredException(CustomException):
3631
def __init__(self):
37-
super().__init__('Token expired.', status_code=401)
32+
super().__init__("Token expired.", status_code=401)

app/core/settings.py

Lines changed: 22 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,36 @@
1-
"""
2-
Settings
3-
"""
4-
5-
from pydantic_settings import BaseSettings, SettingsConfigDict
61
from sqlalchemy import URL
2+
from pydantic import Field
3+
from pydantic_settings import BaseSettings, SettingsConfigDict
74

85

96
class Settings(BaseSettings):
107
model_config = SettingsConfigDict(
11-
env_file='.env', env_file_encoding='utf-8', case_sensitive=True
8+
env_file=".env", env_file_encoding="utf-8", case_sensitive=True
129
)
1310

14-
# APP
15-
APP_PATH: str = '/api'
16-
APP_TITLE: str = 'FastAPI Template'
17-
APP_VERSION: str = 'beta'
18-
APP_SECRET_KEY: str = 'abc'
11+
# App-related environment variables
12+
app_path: str = Field(alias="APP_PATH")
13+
app_title: str = Field(alias="APP_TITLE")
14+
app_secret: str = Field(alias="APP_SECRET")
15+
app_version: str = Field(alias="APP_VERSION")
1916

20-
# DATABASE
21-
DB: str = 'postgres'
22-
DB_HOST: str = 'localhost'
23-
DB_PORT: int = 5432
24-
DB_USER: str = ''
25-
DB_PASSWORD: str = ''
26-
DB_DRIVERNAME: str = 'postgresql+asyncpg'
17+
# Postgres-related environment variables
18+
postgres_db: str = Field(alias="POSTGRES_DB")
19+
postgres_host: str = Field(alias="POSTGRES_HOST")
20+
postgres_port: int = Field(alias="POSTGRES_PORT")
21+
postgres_user: str = Field(alias="POSTGRES_USER")
22+
postgres_password: str = Field(alias="POSTGRES_PASSWORD")
23+
postgres_drivername: str = Field(alias="POSTGRES_DRIVERNAME")
2724

2825
@property
29-
def db_dsn(self) -> str:
26+
def postgres_dsn(self) -> str:
3027
return URL.create(
31-
drivername=self.DB_DRIVERNAME,
32-
username=self.DB_USER,
33-
password=self.DB_PASSWORD,
34-
host=self.DB_HOST,
35-
port=self.DB_PORT,
36-
database=self.DB,
28+
host=self.postgres_host,
29+
port=self.postgres_port,
30+
database=self.postgres_db,
31+
username=self.postgres_user,
32+
password=self.postgres_password,
33+
drivername=self.postgres_drivername,
3734
).render_as_string(hide_password=False)
3835

3936

0 commit comments

Comments
 (0)