diff --git a/.env-template b/.env-template index dd1673b..fb430d9 100644 --- a/.env-template +++ b/.env-template @@ -3,4 +3,8 @@ NETADM_CONFIG__RUN__PORT = 8000 NETADM_CONFIG__RUN__RELOAD = 1 NETADM_CONFIG__DB__URL=postgresql+asyncpg://user:pwd@localhost:5432/app -NETADM_CONFIG__DB__ECHO=1 \ No newline at end of file +NETADM_CONFIG__DB__ECHO=1 + + +NETADM_CONFIG__ACCESS_TOKEN__RESET_PASSWORD_TOKEN_SECRET= +NETADM_CONFIG__ACCESS_TOKEN__VERIFICATION_TOKEN_SECRET= \ No newline at end of file diff --git a/api/routers/__init__.py b/api/routers/__init__.py new file mode 100644 index 0000000..9e58137 --- /dev/null +++ b/api/routers/__init__.py @@ -0,0 +1,10 @@ +from fastapi import APIRouter +from authentication.routers.auth import router as auth_router +from authentication.routers.users import router as users_router + +router = APIRouter() + +router.include_router(auth_router) +router.include_router(users_router) + +__all__ = ["router"] diff --git a/authentication/dependencies/__init__.py b/authentication/dependencies/__init__.py index e69de29..ebfa2a4 100644 --- a/authentication/dependencies/__init__.py +++ b/authentication/dependencies/__init__.py @@ -0,0 +1,11 @@ +from .access_tokens import get_access_tokens_db +from .users import get_users_db +from .user_manager import get_user_manager +from .backend import authentication_backend + +__all__ = ( + "get_access_tokens_db", + "get_users_db", + "get_user_manager", + "authentication_backend", +) diff --git a/authentication/dependencies/access_tokens.py b/authentication/dependencies/access_tokens.py new file mode 100644 index 0000000..0384408 --- /dev/null +++ b/authentication/dependencies/access_tokens.py @@ -0,0 +1,23 @@ +from typing import ( + TYPE_CHECKING, + Annotated, +) + +from fastapi import Depends + +from models import ( + db_helper, + AccessToken, +) + +if TYPE_CHECKING: + from sqlalchemy.ext.asyncio import AsyncSession + + +async def get_access_tokens_db( + session: Annotated[ + "AsyncSession", + Depends(db_helper.session_getter), + ], +): + yield AccessToken.get_db(session=session) diff --git a/authentication/dependencies/backend.py b/authentication/dependencies/backend.py new file mode 100644 index 0000000..c8b3f83 --- /dev/null +++ b/authentication/dependencies/backend.py @@ -0,0 +1,10 @@ +from fastapi_users.authentication import AuthenticationBackend + +from authentication.transport import bearer_transport +from authentication.strategy import get_database_strategy + +authentication_backend = AuthenticationBackend( + name="access-tokens-db", + transport=bearer_transport, + get_strategy=get_database_strategy, +) diff --git a/authentication/dependencies/user_manager.py b/authentication/dependencies/user_manager.py new file mode 100644 index 0000000..651d217 --- /dev/null +++ b/authentication/dependencies/user_manager.py @@ -0,0 +1,19 @@ +from typing import Annotated, TYPE_CHECKING + +from fastapi import Depends + +from authentication.user_manager import UserManager + +from .users import get_users_db + +if TYPE_CHECKING: + from fastapi_users_db_sqlalchemy import SQLAlchemyUserDatabase + + +async def get_user_manager( + users_db: Annotated[ + "SQLAlchemyUserDatabase", + Depends(get_users_db), + ] +): + yield UserManager(users_db) diff --git a/authentication/dependencies/users.py b/authentication/dependencies/users.py new file mode 100644 index 0000000..367775c --- /dev/null +++ b/authentication/dependencies/users.py @@ -0,0 +1,23 @@ +from typing import ( + TYPE_CHECKING, + Annotated, +) + +from fastapi import Depends + +from models import ( + db_helper, + User, +) + +if TYPE_CHECKING: + from sqlalchemy.ext.asyncio import AsyncSession + + +async def get_users_db( + session: Annotated[ + "AsyncSession", + Depends(db_helper.session_getter), + ], +): + yield User.get_db(session=session) diff --git a/authentication/routers/__init__.py b/authentication/routers/__init__.py new file mode 100644 index 0000000..cf46ce1 --- /dev/null +++ b/authentication/routers/__init__.py @@ -0,0 +1,7 @@ +from .auth import router as auth_router +from .users import router as users_router + +__all__ = [ + "auth_router", + "users_router", +] diff --git a/authentication/routers/auth.py b/authentication/routers/auth.py new file mode 100644 index 0000000..95f1563 --- /dev/null +++ b/authentication/routers/auth.py @@ -0,0 +1,40 @@ +from fastapi import APIRouter + +from config import settings +from schemas.user import UserRead, UserCreate + +from .user_router_helper import fastapi_users +from authentication.dependencies import authentication_backend + +router = APIRouter( + prefix=settings.prefix.auth, + tags=["Аuth"], +) + +# /login +# /logout +router.include_router( + router=fastapi_users.get_auth_router( + authentication_backend, + # requires_verification=True, + ), +) +# /register +router.include_router( + router=fastapi_users.get_register_router( + UserRead, + UserCreate, + ), +) + +# /request-verify-token +# /verify +router.include_router( + router=fastapi_users.get_verify_router(UserRead), +) + +# /forgot-password +# /reset-password +router.include_router( + router=fastapi_users.get_reset_password_router(), +) diff --git a/authentication/routers/user_router_helper.py b/authentication/routers/user_router_helper.py new file mode 100644 index 0000000..c3958d6 --- /dev/null +++ b/authentication/routers/user_router_helper.py @@ -0,0 +1,16 @@ +from fastapi_users import FastAPIUsers + +from models import User +from config import settings + + +from authentication.dependencies import get_user_manager +from authentication.dependencies import authentication_backend + +fastapi_users = FastAPIUsers[User, settings.type.UserIdType]( + get_user_manager, + [authentication_backend], +) + +current_active_user = fastapi_users.current_user(active=True) +current_active_superuser = fastapi_users.current_user(active=True, superuser=True) diff --git a/authentication/routers/users.py b/authentication/routers/users.py new file mode 100644 index 0000000..b6d83e3 --- /dev/null +++ b/authentication/routers/users.py @@ -0,0 +1,22 @@ +from fastapi import APIRouter + +from .user_router_helper import fastapi_users +from config import settings +from schemas.user import ( + UserRead, + UserUpdate, +) + +router = APIRouter( + prefix=settings.prefix.users, + tags=["Users"], +) + +# /me +# /{id} +router.include_router( + router=fastapi_users.get_users_router( + UserRead, + UserUpdate, + ), +) diff --git a/authentication/strategy.py b/authentication/strategy.py new file mode 100644 index 0000000..2740e5f --- /dev/null +++ b/authentication/strategy.py @@ -0,0 +1,28 @@ +from typing import ( + TYPE_CHECKING, + Annotated, +) + +from fastapi import Depends +from fastapi_users.authentication.strategy.db import ( + DatabaseStrategy, +) + +from config import settings +from authentication.dependencies import get_access_tokens_db + +if TYPE_CHECKING: + from models import AccessToken + from fastapi_users.authentication.strategy.db import AccessTokenDatabase + + +def get_database_strategy( + access_tokens_db: Annotated[ + "AccessTokenDatabase[AccessToken]", + Depends(get_access_tokens_db), + ], +) -> DatabaseStrategy: + return DatabaseStrategy( + database=access_tokens_db, + lifetime_seconds=settings.access_token.lifetime_seconds, + ) diff --git a/authentication/transport.py b/authentication/transport.py index f9ba03d..e897beb 100644 --- a/authentication/transport.py +++ b/authentication/transport.py @@ -3,6 +3,5 @@ from fastapi_users.authentication import BearerTransport from config import settings bearer_transport = BearerTransport( - # TODO: update url - tokenUrl="auth/jwt/login", + tokenUrl=settings.prefix.bearer_token_url, ) diff --git a/authentication/user_manager.py b/authentication/user_manager.py new file mode 100644 index 0000000..955ce2e --- /dev/null +++ b/authentication/user_manager.py @@ -0,0 +1,53 @@ +import logging +from typing import Optional, TYPE_CHECKING + +from fastapi_users import ( + BaseUserManager, + IntegerIDMixin, +) + +from config import settings + +from models import User + +if TYPE_CHECKING: + from fastapi import Request + + +class UserManager(IntegerIDMixin, BaseUserManager[User, settings.type.UserIdType]): + reset_password_token_secret = settings.access_token.reset_password_token_secret + verification_token_secret = settings.access_token.verification_token_secret + + async def on_after_register( + self, + user: User, + request: Optional["Request"] = None, + ): + logging.warning( + "User %r has registered.", + user.id, + ) + + async def on_after_request_verify( + self, + user: User, + token: str, + request: Optional["Request"] = None, + ): + logging.warning( + "Verification requested for user %r. Verification token: %r", + user.id, + token, + ) + + async def on_after_forgot_password( + self, + user: User, + token: str, + request: Optional["Request"] = None, + ): + logging.warning( + "User %r has forgot their password. Reset token: %r", + user.id, + token, + ) diff --git a/config.py b/config.py index d389af9..a090e42 100644 --- a/config.py +++ b/config.py @@ -51,6 +51,26 @@ class TypesConfig(BaseModel): UserIdType: ClassVar = int +class AccessToken(BaseModel): + lifetime_seconds: int = 3600 + reset_password_token_secret: str + verification_token_secret: str + + +class ApiPrefix(BaseModel): + auth: str = "/auth" + web: str = "/web" + api: str = "/api" + users: str = "/users" + + @property + def bearer_token_url(self) -> str: + # /auth/login + parts = (self.auth, "/login") + path = "".join(parts) + return path.removeprefix("/") + + class Settings(BaseSettings): model_config = SettingsConfigDict( env_file=( @@ -65,6 +85,8 @@ class Settings(BaseSettings): swagger: SwaggerConfig = SwaggerConfig() db: DatabaseConfig type: TypesConfig = TypesConfig() + access_token: AccessToken + prefix: ApiPrefix = ApiPrefix() settings = Settings() diff --git a/main.py b/main.py index 6e69dc7..5a5387c 100644 --- a/main.py +++ b/main.py @@ -6,6 +6,7 @@ from fastapi import FastAPI from fastapi.responses import ORJSONResponse from starlette.staticfiles import StaticFiles from web.routers import router as web_router +from api.routers import router as api_router from models import db_helper @@ -24,9 +25,8 @@ main_app = FastAPI( docs_url=None, ) -main_app.include_router( - web_router, -) +main_app.include_router(api_router) +main_app.include_router(web_router) main_app.mount( diff --git a/models/user.py b/models/user.py index 4cc2cb8..2dacc30 100644 --- a/models/user.py +++ b/models/user.py @@ -8,15 +8,15 @@ from fastapi_users_db_sqlalchemy import ( SQLAlchemyBaseUserTable, SQLAlchemyUserDatabase, ) -from typing import TYPE_CHECKING +from typing import TYPE_CHECKING, Optional if TYPE_CHECKING: from sqlalchemy.ext.asyncio import AsyncSession class User(Base, IdIntPkMixin, SQLAlchemyBaseUserTable[settings.type.UserIdType]): - name: Mapped[str] = mapped_column(nullable=True) - tg_id: Mapped[int] = mapped_column(BigInteger, nullable=True) + name: Mapped[Optional[str]] = mapped_column(nullable=True) + tg_id: Mapped[Optional[int]] = mapped_column(BigInteger, nullable=True) @classmethod def get_db(cls, session: "AsyncSession"): diff --git a/poetry.lock b/poetry.lock index af31fc2..a251e29 100644 --- a/poetry.lock +++ b/poetry.lock @@ -791,6 +791,72 @@ files = [ {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, ] +[[package]] +name = "orjson" +version = "3.10.7" +description = "Fast, correct Python JSON library supporting dataclasses, datetimes, and numpy" +optional = false +python-versions = ">=3.8" +files = [ + {file = "orjson-3.10.7-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:74f4544f5a6405b90da8ea724d15ac9c36da4d72a738c64685003337401f5c12"}, + {file = "orjson-3.10.7-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:34a566f22c28222b08875b18b0dfbf8a947e69df21a9ed5c51a6bf91cfb944ac"}, + {file = "orjson-3.10.7-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bf6ba8ebc8ef5792e2337fb0419f8009729335bb400ece005606336b7fd7bab7"}, + {file = "orjson-3.10.7-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ac7cf6222b29fbda9e3a472b41e6a5538b48f2c8f99261eecd60aafbdb60690c"}, + {file = "orjson-3.10.7-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:de817e2f5fc75a9e7dd350c4b0f54617b280e26d1631811a43e7e968fa71e3e9"}, + {file = "orjson-3.10.7-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:348bdd16b32556cf8d7257b17cf2bdb7ab7976af4af41ebe79f9796c218f7e91"}, + {file = "orjson-3.10.7-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:479fd0844ddc3ca77e0fd99644c7fe2de8e8be1efcd57705b5c92e5186e8a250"}, + {file = "orjson-3.10.7-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:fdf5197a21dd660cf19dfd2a3ce79574588f8f5e2dbf21bda9ee2d2b46924d84"}, + {file = "orjson-3.10.7-cp310-none-win32.whl", hash = "sha256:d374d36726746c81a49f3ff8daa2898dccab6596864ebe43d50733275c629175"}, + {file = "orjson-3.10.7-cp310-none-win_amd64.whl", hash = "sha256:cb61938aec8b0ffb6eef484d480188a1777e67b05d58e41b435c74b9d84e0b9c"}, + {file = "orjson-3.10.7-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:7db8539039698ddfb9a524b4dd19508256107568cdad24f3682d5773e60504a2"}, + {file = "orjson-3.10.7-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:480f455222cb7a1dea35c57a67578848537d2602b46c464472c995297117fa09"}, + {file = "orjson-3.10.7-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8a9c9b168b3a19e37fe2778c0003359f07822c90fdff8f98d9d2a91b3144d8e0"}, + {file = "orjson-3.10.7-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8de062de550f63185e4c1c54151bdddfc5625e37daf0aa1e75d2a1293e3b7d9a"}, + {file = "orjson-3.10.7-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6b0dd04483499d1de9c8f6203f8975caf17a6000b9c0c54630cef02e44ee624e"}, + {file = "orjson-3.10.7-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b58d3795dafa334fc8fd46f7c5dc013e6ad06fd5b9a4cc98cb1456e7d3558bd6"}, + {file = "orjson-3.10.7-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:33cfb96c24034a878d83d1a9415799a73dc77480e6c40417e5dda0710d559ee6"}, + {file = "orjson-3.10.7-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:e724cebe1fadc2b23c6f7415bad5ee6239e00a69f30ee423f319c6af70e2a5c0"}, + {file = "orjson-3.10.7-cp311-none-win32.whl", hash = "sha256:82763b46053727a7168d29c772ed5c870fdae2f61aa8a25994c7984a19b1021f"}, + {file = "orjson-3.10.7-cp311-none-win_amd64.whl", hash = "sha256:eb8d384a24778abf29afb8e41d68fdd9a156cf6e5390c04cc07bbc24b89e98b5"}, + {file = "orjson-3.10.7-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:44a96f2d4c3af51bfac6bc4ef7b182aa33f2f054fd7f34cc0ee9a320d051d41f"}, + {file = "orjson-3.10.7-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:76ac14cd57df0572453543f8f2575e2d01ae9e790c21f57627803f5e79b0d3c3"}, + {file = "orjson-3.10.7-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bdbb61dcc365dd9be94e8f7df91975edc9364d6a78c8f7adb69c1cdff318ec93"}, + {file = "orjson-3.10.7-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b48b3db6bb6e0a08fa8c83b47bc169623f801e5cc4f24442ab2b6617da3b5313"}, + {file = "orjson-3.10.7-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:23820a1563a1d386414fef15c249040042b8e5d07b40ab3fe3efbfbbcbcb8864"}, + {file = "orjson-3.10.7-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a0c6a008e91d10a2564edbb6ee5069a9e66df3fbe11c9a005cb411f441fd2c09"}, + {file = "orjson-3.10.7-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d352ee8ac1926d6193f602cbe36b1643bbd1bbcb25e3c1a657a4390f3000c9a5"}, + {file = "orjson-3.10.7-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:d2d9f990623f15c0ae7ac608103c33dfe1486d2ed974ac3f40b693bad1a22a7b"}, + {file = "orjson-3.10.7-cp312-none-win32.whl", hash = "sha256:7c4c17f8157bd520cdb7195f75ddbd31671997cbe10aee559c2d613592e7d7eb"}, + {file = "orjson-3.10.7-cp312-none-win_amd64.whl", hash = "sha256:1d9c0e733e02ada3ed6098a10a8ee0052dd55774de3d9110d29868d24b17faa1"}, + {file = "orjson-3.10.7-cp313-cp313-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:77d325ed866876c0fa6492598ec01fe30e803272a6e8b10e992288b009cbe149"}, + {file = "orjson-3.10.7-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9ea2c232deedcb605e853ae1db2cc94f7390ac776743b699b50b071b02bea6fe"}, + {file = "orjson-3.10.7-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:3dcfbede6737fdbef3ce9c37af3fb6142e8e1ebc10336daa05872bfb1d87839c"}, + {file = "orjson-3.10.7-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:11748c135f281203f4ee695b7f80bb1358a82a63905f9f0b794769483ea854ad"}, + {file = "orjson-3.10.7-cp313-none-win32.whl", hash = "sha256:a7e19150d215c7a13f39eb787d84db274298d3f83d85463e61d277bbd7f401d2"}, + {file = "orjson-3.10.7-cp313-none-win_amd64.whl", hash = "sha256:eef44224729e9525d5261cc8d28d6b11cafc90e6bd0be2157bde69a52ec83024"}, + {file = "orjson-3.10.7-cp38-cp38-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:6ea2b2258eff652c82652d5e0f02bd5e0463a6a52abb78e49ac288827aaa1469"}, + {file = "orjson-3.10.7-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:430ee4d85841e1483d487e7b81401785a5dfd69db5de01314538f31f8fbf7ee1"}, + {file = "orjson-3.10.7-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4b6146e439af4c2472c56f8540d799a67a81226e11992008cb47e1267a9b3225"}, + {file = "orjson-3.10.7-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:084e537806b458911137f76097e53ce7bf5806dda33ddf6aaa66a028f8d43a23"}, + {file = "orjson-3.10.7-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4829cf2195838e3f93b70fd3b4292156fc5e097aac3739859ac0dcc722b27ac0"}, + {file = "orjson-3.10.7-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1193b2416cbad1a769f868b1749535d5da47626ac29445803dae7cc64b3f5c98"}, + {file = "orjson-3.10.7-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:4e6c3da13e5a57e4b3dca2de059f243ebec705857522f188f0180ae88badd354"}, + {file = "orjson-3.10.7-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:c31008598424dfbe52ce8c5b47e0752dca918a4fdc4a2a32004efd9fab41d866"}, + {file = "orjson-3.10.7-cp38-none-win32.whl", hash = "sha256:7122a99831f9e7fe977dc45784d3b2edc821c172d545e6420c375e5a935f5a1c"}, + {file = "orjson-3.10.7-cp38-none-win_amd64.whl", hash = "sha256:a763bc0e58504cc803739e7df040685816145a6f3c8a589787084b54ebc9f16e"}, + {file = "orjson-3.10.7-cp39-cp39-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:e76be12658a6fa376fcd331b1ea4e58f5a06fd0220653450f0d415b8fd0fbe20"}, + {file = "orjson-3.10.7-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ed350d6978d28b92939bfeb1a0570c523f6170efc3f0a0ef1f1df287cd4f4960"}, + {file = "orjson-3.10.7-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:144888c76f8520e39bfa121b31fd637e18d4cc2f115727865fdf9fa325b10412"}, + {file = "orjson-3.10.7-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:09b2d92fd95ad2402188cf51573acde57eb269eddabaa60f69ea0d733e789fe9"}, + {file = "orjson-3.10.7-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5b24a579123fa884f3a3caadaed7b75eb5715ee2b17ab5c66ac97d29b18fe57f"}, + {file = "orjson-3.10.7-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e72591bcfe7512353bd609875ab38050efe3d55e18934e2f18950c108334b4ff"}, + {file = "orjson-3.10.7-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:f4db56635b58cd1a200b0a23744ff44206ee6aa428185e2b6c4a65b3197abdcd"}, + {file = "orjson-3.10.7-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:0fa5886854673222618638c6df7718ea7fe2f3f2384c452c9ccedc70b4a510a5"}, + {file = "orjson-3.10.7-cp39-none-win32.whl", hash = "sha256:8272527d08450ab16eb405f47e0f4ef0e5ff5981c3d82afe0efd25dcbef2bcd2"}, + {file = "orjson-3.10.7-cp39-none-win_amd64.whl", hash = "sha256:974683d4618c0c7dbf4f69c95a979734bf183d0658611760017f6e70a145af58"}, + {file = "orjson-3.10.7.tar.gz", hash = "sha256:75ef0640403f945f3a1f9f6400686560dbfb0fb5b16589ad62cd477043c4eee3"}, +] + [[package]] name = "packaging" version = "24.1" @@ -1536,4 +1602,4 @@ files = [ [metadata] lock-version = "2.0" python-versions = "^3.12" -content-hash = "48335dd82829883c1d2ac4b19b8ae09d8455bf3b9add9aa6bd83bb07e61336f2" +content-hash = "41f38830f56655c559bf42be3e15ee645b33259384d5cd3a66632f839a34c3f4" diff --git a/pyproject.toml b/pyproject.toml index 28c02c1..8d57328 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -16,6 +16,7 @@ alembic = "^1.13.3" asyncpg = "^0.29.0" sqlalchemy = {extras = ["asyncio"], version = "^2.0.35"} fastapi-users = {extras = ["sqlalchemy"], version = "^13.0.0"} +orjson = "^3.10.7" diff --git a/schemas/__init__.py b/schemas/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/schemas/user.py b/schemas/user.py new file mode 100644 index 0000000..fde7255 --- /dev/null +++ b/schemas/user.py @@ -0,0 +1,19 @@ +from fastapi_users import schemas + +from config import settings +from typing import Optional + + +class UserRead(schemas.BaseUser[settings.type.UserIdType]): + name: Optional[str] = None + tg_id: Optional[int] = None + + +class UserCreate(schemas.BaseUserCreate): + name: Optional[str] = None + tg_id: Optional[int] = None + + +class UserUpdate(schemas.BaseUserUpdate): + name: Optional[str] = None + tg_id: Optional[int] = None