From d1b91abf05e8ec5d30c78c2ae56de0caac916d98 Mon Sep 17 00:00:00 2001 From: coder-bot Date: Mon, 17 Aug 2026 20:25:43 +0000 Subject: [PATCH] WIP checkpoint: Scaffold FastAPI backend project structure --- backend/app/dependencies.py | 50 +++++++++++++++++++++++++++++++++++ backend/app/models/asset.py | 10 ++++--- backend/app/models/session.py | 2 +- 3 files changed, 58 insertions(+), 4 deletions(-) create mode 100644 backend/app/dependencies.py diff --git a/backend/app/dependencies.py b/backend/app/dependencies.py new file mode 100644 index 0000000..89811b8 --- /dev/null +++ b/backend/app/dependencies.py @@ -0,0 +1,50 @@ +"""Shared FastAPI dependencies, including JWT-based auth.""" + +from typing import Annotated + +from fastapi import Depends, HTTPException, status +from fastapi.security import OAuth2PasswordBearer +from jose import JWTError, jwt +from sqlalchemy import select +from sqlalchemy.ext.asyncio import AsyncSession + +from app.config import settings +from app.database import get_db +from app.models.user import User + +oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/api/auth/login") + + +async def get_current_user( + token: Annotated[str, Depends(oauth2_scheme)], + db: Annotated[AsyncSession, Depends(get_db)], +) -> User: + """Resolve the JWT bearer token to a User, or raise 401.""" + credentials_exception = HTTPException( + status_code=status.HTTP_401_UNAUTHORIZED, + detail="Could not validate credentials", + headers={"WWW-Authenticate": "Bearer"}, + ) + try: + payload = jwt.decode( + token, settings.SECRET_KEY, algorithms=[settings.ALGORITHM] + ) + user_id: str | None = payload.get("sub") + if user_id is None: + raise credentials_exception + except JWTError: + raise credentials_exception + + result = await db.execute(select(User).where(User.id == int(user_id))) + user = result.scalar_one_or_none() + if user is None: + raise credentials_exception + if not user.is_active: + raise HTTPException( + status_code=status.HTTP_403_FORBIDDEN, + detail="User account is inactive", + ) + return user + + +CurrentUser = Annotated[User, Depends(get_current_user)] diff --git a/backend/app/models/asset.py b/backend/app/models/asset.py index ba27c53..2955515 100644 --- a/backend/app/models/asset.py +++ b/backend/app/models/asset.py @@ -1,4 +1,5 @@ -from sqlalchemy import Column, Integer, String, ForeignKey, Text, DateTime, Enum as SAEnum +from sqlalchemy import Column, Integer, String, ForeignKey, Text, DateTime, Boolean +from sqlalchemy.dialects.postgresql import ENUM from sqlalchemy.orm import relationship from sqlalchemy.sql import func import enum @@ -21,14 +22,17 @@ class Asset(Base): id = Column(Integer, primary_key=True, index=True) name = Column(String(255), nullable=False) description = Column(Text, nullable=True) - asset_type = Column(SAEnum(AssetType), nullable=False) + asset_type = Column( + ENUM(AssetType, name="asset_type", create_type=False), + nullable=False, + ) file_path = Column(String(512), nullable=False) file_size = Column(Integer, nullable=True) mime_type = Column(String(128), nullable=True) metadata_json = Column(Text, nullable=True) world_id = Column(Integer, ForeignKey("worlds.id"), nullable=True) owner_id = Column(Integer, ForeignKey("users.id"), nullable=False) - is_public = Column(Integer, default=0, nullable=False) + is_public = Column(Boolean, default=False, nullable=False) created_at = Column(DateTime(timezone=True), server_default=func.now()) updated_at = Column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now()) diff --git a/backend/app/models/session.py b/backend/app/models/session.py index 9d804bf..7a7d33a 100644 --- a/backend/app/models/session.py +++ b/backend/app/models/session.py @@ -1,6 +1,6 @@ import uuid -from sqlalchemy import Column, String, DateTime, ForeignKey, Boolean +from sqlalchemy import Column, Integer, String, DateTime, ForeignKey, Boolean from sqlalchemy.orm import relationship from sqlalchemy.sql import func from app.database import Base