Merge origin/main into feature/upload-download-apis

Resolved conflicts in:
- backend/app/routes.py: Combined imports (Query, Header, Response) and schemas (pagination + resumable upload)
- backend/app/schemas.py: Kept all schemas (pagination + resumable upload)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Mondo Diaz
2025-12-11 17:18:21 -06:00
11 changed files with 911 additions and 175 deletions

View File

@@ -1,8 +1,9 @@
from fastapi import APIRouter, Depends, HTTPException, UploadFile, File, Form, Request, Header, Response
from fastapi import APIRouter, Depends, HTTPException, UploadFile, File, Form, Request, Query, Header, Response
from fastapi.responses import StreamingResponse
from sqlalchemy.orm import Session
from sqlalchemy import or_
from sqlalchemy import or_, func
from typing import List, Optional
import math
import re
import io
import hashlib
@@ -18,6 +19,7 @@ from .schemas import (
UploadResponse,
ConsumerResponse,
HealthResponse,
PaginatedResponse, PaginationMeta,
ResumableUploadInitRequest,
ResumableUploadInitResponse,
ResumableUploadPartResponse,
@@ -48,13 +50,44 @@ def health_check():
# Project routes
@router.get("/api/v1/projects", response_model=List[ProjectResponse])
def list_projects(request: Request, db: Session = Depends(get_db)):
@router.get("/api/v1/projects", response_model=PaginatedResponse[ProjectResponse])
def list_projects(
request: Request,
page: int = Query(default=1, ge=1, description="Page number"),
limit: int = Query(default=20, ge=1, le=100, description="Items per page"),
search: Optional[str] = Query(default=None, description="Search by project name"),
db: Session = Depends(get_db),
):
user_id = get_user_id(request)
projects = db.query(Project).filter(
# Base query - filter by access
query = db.query(Project).filter(
or_(Project.is_public == True, Project.created_by == user_id)
).order_by(Project.name).all()
return projects
)
# Apply search filter (case-insensitive)
if search:
query = query.filter(func.lower(Project.name).contains(search.lower()))
# Get total count before pagination
total = query.count()
# Apply pagination
offset = (page - 1) * limit
projects = query.order_by(Project.name).offset(offset).limit(limit).all()
# Calculate total pages
total_pages = math.ceil(total / limit) if total > 0 else 1
return PaginatedResponse(
items=projects,
pagination=PaginationMeta(
page=page,
limit=limit,
total=total,
total_pages=total_pages,
),
)
@router.post("/api/v1/projects", response_model=ProjectResponse)