diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..9a9a2df --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1 @@ +* @amrtgaber diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index de9d4d3..2b16138 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -6,6 +6,9 @@ on: pull_request: branches: [main] +permissions: + contents: read + jobs: lint: runs-on: ubuntu-latest diff --git a/app/auth/users.py b/app/auth/users.py index d1ec519..5f48b6b 100644 --- a/app/auth/users.py +++ b/app/auth/users.py @@ -81,5 +81,19 @@ async def get_user_manager( fastapi_users = FastAPIUsers[User, UUID](get_user_manager, [auth_backend]) -current_active_user = fastapi_users.current_user(active=True) -current_superuser = fastapi_users.current_user(active=True, superuser=True) +_fastapi_users_current_active = fastapi_users.current_user(active=True) +_fastapi_users_current_superuser = fastapi_users.current_user(active=True, superuser=True) + + +async def current_active_user( + user: User = Depends(_fastapi_users_current_active), +) -> User: + structlog.contextvars.bind_contextvars(user_id=str(user.id)) + return user + + +async def current_superuser( + user: User = Depends(_fastapi_users_current_superuser), +) -> User: + structlog.contextvars.bind_contextvars(user_id=str(user.id)) + return user diff --git a/app/main.py b/app/main.py index d1db77b..1b23fd3 100644 --- a/app/main.py +++ b/app/main.py @@ -48,6 +48,20 @@ app.state.limiter = limiter app.add_exception_handler(RateLimitExceeded, _rate_limit_exceeded_handler) +MAX_REQUEST_BODY_SIZE = 1_048_576 # 1 MB + + +@app.middleware("http") +async def limit_request_body_size(request: Request, call_next) -> Response: + content_length = request.headers.get("content-length") + if content_length and int(content_length) > MAX_REQUEST_BODY_SIZE: + return JSONResponse( + status_code=413, + content={"detail": "Request body too large"}, + ) + return await call_next(request) + + # --- Auth routes --- # Custom refresh/logout routes (included before FastAPI-Users so /auth/jwt/logout is shadowed) app.include_router(auth_refresh_router) diff --git a/app/schemas/note.py b/app/schemas/note.py index 95c1333..1524547 100644 --- a/app/schemas/note.py +++ b/app/schemas/note.py @@ -8,7 +8,11 @@ class NoteBase(BaseModel): """Base schema with shared note fields.""" title: str = Field(..., min_length=1, max_length=200, examples=["Meeting notes"]) - body: str | None = Field(None, examples=["Discussed project timeline and milestones."]) + body: str | None = Field( + None, + max_length=10_000, + examples=["Discussed project timeline and milestones."], + ) class NoteCreate(NoteBase): @@ -21,7 +25,7 @@ class NoteUpdate(BaseModel): """Schema for updating a note. All fields optional.""" title: str | None = Field(None, min_length=1, max_length=200) - body: str | None = None + body: str | None = Field(None, max_length=10_000) class NoteRead(NoteBase): diff --git a/app/schemas/user.py b/app/schemas/user.py index e05b22e..914041a 100644 --- a/app/schemas/user.py +++ b/app/schemas/user.py @@ -1,6 +1,7 @@ from uuid import UUID from fastapi_users import schemas +from pydantic import Field class UserRead(schemas.BaseUser[UUID]): @@ -13,10 +14,10 @@ class UserRead(schemas.BaseUser[UUID]): class UserCreate(schemas.BaseUserCreate): """Schema for creating a new user.""" - name: str | None = None + name: str | None = Field(None, max_length=100) class UserUpdate(schemas.BaseUserUpdate): """Schema for updating user data.""" - name: str | None = None + name: str | None = Field(None, max_length=100)