From 0bff8f1911ecc45fcbec3bcadc55fd21f4d9dd8b Mon Sep 17 00:00:00 2001 From: MAGICX Date: Fri, 6 Dec 2024 05:25:38 +0500 Subject: [PATCH] feat: I wrote it all over again --- backend/.gitignore | 2 +- backend/Pipfile | 25 ++++++++++++ backend/app/api/__init__.py | 6 --- backend/app/api/v1/endpoints/__init__.py | 0 backend/app/api/v1/endpoints/overlay.py | 23 ----------- backend/app/crud/__init__.py | 0 backend/app/crud/overlay.py | 27 ------------- backend/app/db.py | 20 ++++++++++ backend/app/db/__init__.py | 0 backend/app/db/session.py | 26 ------------- backend/app/models/__init___.py | 0 backend/app/models/overlay.py | 31 --------------- backend/app/models/overlays.py | 20 ++++++++++ backend/app/routers/__init__.py | 6 +++ backend/app/routers/overlays.py | 36 +++++++++++++++++ backend/app/schemas/__init__.py | 0 backend/app/schemas/overlay.py | 22 ----------- backend/app/settings.py | 38 ++++++++---------- backend/main.py | 18 ++++----- backend/pyproject.toml | 49 ------------------------ 20 files changed, 130 insertions(+), 219 deletions(-) create mode 100644 backend/Pipfile delete mode 100644 backend/app/api/__init__.py delete mode 100644 backend/app/api/v1/endpoints/__init__.py delete mode 100644 backend/app/api/v1/endpoints/overlay.py delete mode 100644 backend/app/crud/__init__.py delete mode 100644 backend/app/crud/overlay.py create mode 100644 backend/app/db.py delete mode 100644 backend/app/db/__init__.py delete mode 100644 backend/app/db/session.py delete mode 100644 backend/app/models/__init___.py delete mode 100644 backend/app/models/overlay.py create mode 100644 backend/app/models/overlays.py create mode 100644 backend/app/routers/__init__.py create mode 100644 backend/app/routers/overlays.py delete mode 100644 backend/app/schemas/__init__.py delete mode 100644 backend/app/schemas/overlay.py delete mode 100644 backend/pyproject.toml diff --git a/backend/.gitignore b/backend/.gitignore index 72da2a7..8703ba4 100644 --- a/backend/.gitignore +++ b/backend/.gitignore @@ -1,4 +1,4 @@ uv.lock sqlite.db .venv -.ruff_cache \ No newline at end of file +Pipfile.lock diff --git a/backend/Pipfile b/backend/Pipfile new file mode 100644 index 0000000..f44a80d --- /dev/null +++ b/backend/Pipfile @@ -0,0 +1,25 @@ +[[source]] +url = "https://pypi.org/simple" +verify_ssl = true +name = "pypi" + +[packages] +aiosqlite = "*" +alembic = "*" +asyncpg = "*" +fastapi = {extras = ["standard"], version = "*"} +greenlet = "*" +psycopg2-binary = "*" +python-dotenv = "*" +sqlalchemy = "*" +sqlmodel = "*" +uuid = "*" +uvicorn = "*" + +[dev-packages] + +[requires] +python_version = "3.13" + +[scripts] +dev = "fastapi dev main.py" \ No newline at end of file diff --git a/backend/app/api/__init__.py b/backend/app/api/__init__.py deleted file mode 100644 index 6b89f87..0000000 --- a/backend/app/api/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -from fastapi import APIRouter - -from app.api.v1.endpoints import overlay - -api_router = APIRouter() -api_router.include_router(overlay.router, prefix="/overlays", tags=["overlays"]) diff --git a/backend/app/api/v1/endpoints/__init__.py b/backend/app/api/v1/endpoints/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/backend/app/api/v1/endpoints/overlay.py b/backend/app/api/v1/endpoints/overlay.py deleted file mode 100644 index ea6585a..0000000 --- a/backend/app/api/v1/endpoints/overlay.py +++ /dev/null @@ -1,23 +0,0 @@ -from fastapi import APIRouter, Depends -from sqlmodel.ext.asyncio.session import AsyncSession - -from app.crud import overlay as crud -from app.db.session import get_session -from app.schemas.overlay import OverlaySchema - -router = APIRouter() - - -@router.get("/", response_model=list[OverlaySchema]) -async def get_overlays(session: AsyncSession = Depends(get_session)): - return await crud.get_overlays(session) - - -@router.get("/{overlay_id}", response_model=OverlaySchema) -async def get_overlay(overlay_id: str, session: AsyncSession = Depends(get_session)): - return await crud.get_overlay(overlay_id, session) - - -@router.post("/", response_model=OverlaySchema) -async def create_overlay(overlay: OverlaySchema, session: AsyncSession = Depends(get_session)): - return await crud.create_overlay(overlay, session) diff --git a/backend/app/crud/__init__.py b/backend/app/crud/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/backend/app/crud/overlay.py b/backend/app/crud/overlay.py deleted file mode 100644 index 20b4800..0000000 --- a/backend/app/crud/overlay.py +++ /dev/null @@ -1,27 +0,0 @@ -from fastapi import HTTPException -from sqlmodel import select -from sqlmodel.ext.asyncio.session import AsyncSession - -from app.models.overlay import Overlay -from app.schemas.overlay import OverlayCreate - - -async def get_overlays(session: AsyncSession) -> list[Overlay]: - result = await session.execute(select(Overlay)) - overlays = result.scalars().all() - return overlays - - -async def get_overlay(overlay_id: str, session: AsyncSession) -> Overlay: - overlay = await session.get(Overlay, overlay_id) - if overlay is None: - raise HTTPException(status_code=404, detail="Overlay not found") - return overlay - - -async def add_overlay(overlay_data: OverlayCreate, session: AsyncSession) -> Overlay: - new_overlay = Overlay(**overlay_data.dict()) - session.add(new_overlay) - await session.commit() - await session.refresh(new_overlay) - return new_overlay diff --git a/backend/app/db.py b/backend/app/db.py new file mode 100644 index 0000000..02178f5 --- /dev/null +++ b/backend/app/db.py @@ -0,0 +1,20 @@ +from sqlalchemy.ext.asyncio import create_async_engine +from sqlalchemy.orm import sessionmaker +from sqlmodel import SQLModel +from sqlmodel.ext.asyncio.session import AsyncSession + +from app.settings import DATABASE_URL +from app.models import overlays + +engine = create_async_engine(DATABASE_URL, echo=False) + + +async def init_db(): + async with engine.begin() as conn: + await conn.run_sync(SQLModel.metadata.create_all) + + +async def get_session(): + async_session = sessionmaker(bind=engine, class_=AsyncSession, expire_on_commit=False) + async with async_session() as session: + yield session diff --git a/backend/app/db/__init__.py b/backend/app/db/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/backend/app/db/session.py b/backend/app/db/session.py deleted file mode 100644 index 330e3b4..0000000 --- a/backend/app/db/session.py +++ /dev/null @@ -1,26 +0,0 @@ -from sqlalchemy.ext.asyncio import create_async_engine, async_sessionmaker -from sqlmodel import SQLModel -from sqlmodel.ext.asyncio.session import AsyncSession - -from app.settings import settings - -if settings.DEBUG: - engine = create_async_engine(settings.DEBUG_DATABASE_URL, echo=False, future=True) -else: - engine = create_async_engine(settings.DATABASE_URL, echo=False, future=True) - -AsyncSessionLocal = async_sessionmaker( - bind=engine, - class_=AsyncSession, - expire_on_commit=False, -) - - -async def get_session() -> AsyncSession: - async with AsyncSessionLocal() as session: - yield session - - -async def init_db(): - async with engine.begin() as conn: - await conn.run_sync(SQLModel.metadata.create_all) diff --git a/backend/app/models/__init___.py b/backend/app/models/__init___.py deleted file mode 100644 index e69de29..0000000 diff --git a/backend/app/models/overlay.py b/backend/app/models/overlay.py deleted file mode 100644 index 4f4886a..0000000 --- a/backend/app/models/overlay.py +++ /dev/null @@ -1,31 +0,0 @@ -from sqlmodel import SQLModel, Field -from uuid import UUID, uuid4 - - -class OverlayBase(SQLModel): - riot_id: str - hdev_api_key: str - # hdev_key: str - # text_color: str - # primary_color: str - # bg_color: str - # progress_rank_color: str - # progress_rank_bg_color: str - # alpha_bg: bool - # alpha_grad_bg: bool - # wl_stat: bool - # progress_rank: bool - # last_match_points: bool - - -class Overlay(OverlayBase, table=True): - uuid: UUID = Field( - default_factory=uuid4, - primary_key=True, - index=True, - nullable=False, - ) - - -class OverlayCreate(OverlayBase): - pass diff --git a/backend/app/models/overlays.py b/backend/app/models/overlays.py new file mode 100644 index 0000000..cc23769 --- /dev/null +++ b/backend/app/models/overlays.py @@ -0,0 +1,20 @@ +import uuid + +from sqlmodel import SQLModel, Field + + +class OverlayBase(SQLModel): + riot_id: str + hdev_api_key: str + + +class Overlay(OverlayBase, table=True): + id: uuid.UUID = Field( + default_factory=uuid.uuid4, + primary_key=True, + nullable=False, + ) + + +class OverlayCreate(OverlayBase): + pass diff --git a/backend/app/routers/__init__.py b/backend/app/routers/__init__.py new file mode 100644 index 0000000..d904aa0 --- /dev/null +++ b/backend/app/routers/__init__.py @@ -0,0 +1,6 @@ +from fastapi import APIRouter + +from app.routers import overlays + +api_router = APIRouter() +api_router.include_router(overlays.router, prefix="/overlay", tags=["overlay"]) \ No newline at end of file diff --git a/backend/app/routers/overlays.py b/backend/app/routers/overlays.py new file mode 100644 index 0000000..720c542 --- /dev/null +++ b/backend/app/routers/overlays.py @@ -0,0 +1,36 @@ +import uuid +from fastapi import APIRouter, Depends, HTTPException +from sqlmodel import select +from sqlmodel.ext.asyncio.session import AsyncSession + +from app.db import get_session +from app.models.overlays import Overlay, OverlayCreate + +router = APIRouter() + + +@router.get('/', response_model=list[Overlay]) +async def get_overlays(session: AsyncSession = Depends(get_session)): + result = await session.exec(select(Overlay)) + overlays = result.all() + return [Overlay(id=overlay.id, riot_id=overlay.riot_id, hdev_api_key=overlay.hdev_api_key) for overlay in overlays] + +@router.get("/{overlay_id}") +async def get_overlay(overlay_id: uuid.UUID, session: AsyncSession = Depends(get_session)): + statement = select(Overlay).where(Overlay.id == overlay_id) + result = await session.exec(statement) + overlay = result.first() + + if overlay is None: + raise HTTPException(status_code=404, detail="Overlay not found") + + return overlay + + +@router.post("/") +async def create_overlay(overlay: OverlayCreate, session: AsyncSession = Depends(get_session)): + overlay = Overlay(riot_id=overlay.riot_id, hdev_api_key=overlay.hdev_api_key) + session.add(overlay) + await session.commit() + await session.refresh(overlay) + return overlay \ No newline at end of file diff --git a/backend/app/schemas/__init__.py b/backend/app/schemas/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/backend/app/schemas/overlay.py b/backend/app/schemas/overlay.py deleted file mode 100644 index e5c398f..0000000 --- a/backend/app/schemas/overlay.py +++ /dev/null @@ -1,22 +0,0 @@ -import uuid as uuid_pkg -from pydantic import BaseModel - -from app.models.overlay import OverlayBase - - -class OverlaySchema(BaseModel): - uuid: uuid_pkg.UUID - riot_id: str - hdev_api_key: str - - model_config = {"from_attributes": True} - - -class OverlayCreate(OverlayBase): - pass - - -class OverlayRead(OverlayBase): - uuid: uuid_pkg.UUID - - model_config = {"from_attributes": True} diff --git a/backend/app/settings.py b/backend/app/settings.py index 37fc270..62dd5a3 100644 --- a/backend/app/settings.py +++ b/backend/app/settings.py @@ -1,35 +1,27 @@ -import secrets from os import environ -from typing import Literal - -from pydantic_settings import BaseSettings, SettingsConfigDict +from pathlib import Path +from dotenv import load_dotenv def str_to_bool(value: str) -> bool: return value.lower() in ("true", "1", "yes") +BASE_DIR: Path = Path(__file__).resolve().parent.parent.parent -class Settings(BaseSettings): - model_config = SettingsConfigDict( - env_file="../.env", - env_ignore_empty=True, - extra="ignore", - ) - API_V1_STR: str = "/api/v1" - SECRET_KEY: str = secrets.token_urlsafe(32) - ACCESS_TOKEN_EXPIRE_MINUTES: int = 60 * 24 * 8 - FRONTEND_HOST: str = "http://localhost:5173" - ENVIRONMENT: Literal["local", "staging", "production"] = "local" - - PROJECT_NAME: str = environ.get("PROJECT_NAME") +dotenv_file: Path = BASE_DIR / ".env" +if dotenv_file.is_file(): + load_dotenv(dotenv_file) - DEBUG: bool = str_to_bool(environ.get("DEBUG", "False")) +PROJECT_NAME: str = environ.get("PROJECT_NAME") - PROJECT_NAME: str = environ.get("PROJECT_NAME") - VERSION: str = environ.get("VERSION") +DEBUG: bool = str_to_bool(environ.get("DEBUG", "False")) - DATABASE_URL: str = environ.get("DATABASE_URL") - DEBUG_DATABASE_URL: str = environ.get("DEBUG_DATABASE_URL") +PROJECT_NAME: str = environ.get("PROJECT_NAME") +VERSION: str = environ.get("VERSION") +DATABASE_LOGIN: str = environ.get("DATABASE_LOGIN") +DATABASE_PASSWORD: str = environ.get("DATABASE_PASSWORD") +DATABASE_NAME: str = environ.get("DATABASE_NAME") +DATABASE_PORT: int = int(environ.get("DATABASE_PORT", 5432)) -settings = Settings() +DATABASE_URL: str = f"postgresql+asyncpg://{DATABASE_LOGIN}:{DATABASE_PASSWORD}@localhost:{DATABASE_PORT}/{DATABASE_NAME}" diff --git a/backend/main.py b/backend/main.py index d092f01..96e6ed0 100644 --- a/backend/main.py +++ b/backend/main.py @@ -1,21 +1,13 @@ import uvicorn from fastapi import FastAPI -from contextlib import asynccontextmanager from fastapi.middleware.cors import CORSMiddleware -from app.api import api_router -from app.db.session import init_db -from app.settings import settings +from app import settings +from app.routers import api_router -@asynccontextmanager -async def lifespan(app: FastAPI): - await init_db() - yield - app = FastAPI( title=settings.PROJECT_NAME, version=settings.VERSION, - lifespan=lifespan ) app.add_middleware( @@ -28,5 +20,9 @@ async def lifespan(app: FastAPI): app.include_router(api_router, prefix="/api") +@app.get("/") +async def root(): + return {"message": "Hello World"} + if __name__ == "__main__": - uvicorn.run(app="main:app", reload=True) + uvicorn.run(app="main:app", reload=True, port=8080) diff --git a/backend/pyproject.toml b/backend/pyproject.toml deleted file mode 100644 index 365c687..0000000 --- a/backend/pyproject.toml +++ /dev/null @@ -1,49 +0,0 @@ -[project] -name = "backend" -version = "0.1.0" -description = "" -requires-python = ">=3.12" -dependencies = [ - "aiosqlite>=0.20.0", - "alembic>=1.14.0", - "asyncpg>=0.30.0", - "fastapi[standard]>=0.115.5", - "greenlet>=3.1.1", - "psycopg2-binary>=2.9.10", - "pydantic-settings>=2.6.1", - "python-dotenv>=1.0.1", - "sqlalchemy>=2.0.36", - "sqlmodel>=0.0.22", - "uuid>=1.30", - "uvicorn>=0.32.1", -] - -[tool.uv] -dev-dependencies = [ - "ruff<1.0.0,>=0.2.2", -] - -[tool.ruff] -target-version = "py312" -exclude = ["alembic"] - -[tool.ruff.lint] -select = [ - "E", - "W", - "F", - "I", - "B", - "C4", - "UP", - "ARG001", -] -ignore = [ - "E501", - "B008", - "W191", - "B904", -] - -[tool.ruff.lint.pyupgrade] -keep-runtime-typing = true