from fastapi import APIRouter, Depends, HTTPException, UploadFile, File, Form from sqlalchemy.ext.asyncio import AsyncSession from app.core.deps import ensure_class_access, ensure_class_permission, require_role, resolve_class_id_for_user from app.db.database import get_db from app.db.models import User from app.schemas.resource import ResourceCreate, ResourceOut from app.schemas.common import PageResponse from app.services.resource_service import ( create_resource, list_resources, get_resource_by_id, increment_download_count, delete_resource, ) from app.services.cos_service import upload_file router = APIRouter(prefix="/api/resources", tags=["resources"]) ALLOWED_FILE_TYPES = { "application/pdf", "application/msword", "application/vnd.openxmlformats-officedocument.wordprocessingml.document", "application/vnd.ms-excel", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", "application/vnd.ms-powerpoint", "application/vnd.openxmlformats-officedocument.presentationml.presentation", "application/zip", "text/plain", "image/jpeg", "image/png", "image/gif", "image/webp", } @router.get("/", response_model=PageResponse[ResourceOut]) async def get_resources( page: int = 1, page_size: int = 20, category: str | None = None, class_id: int | None = None, user: User = Depends(require_role("super_admin", "teacher", "student")), db: AsyncSession = Depends(get_db), ): effective_class_id = resolve_class_id_for_user(user, class_id) if effective_class_id is None: return PageResponse(items=[], total=0, page=page, page_size=page_size, total_pages=0) ensure_class_access(user, effective_class_id) resources, total = await list_resources(db, effective_class_id, category, page, page_size) total_pages = (total + page_size - 1) // page_size items = [] for r in resources: items.append( ResourceOut( id=r.id, class_id=r.class_id, uploader_id=r.uploader_id, uploader_name=r.uploader.name if r.uploader else "Unknown", title=r.title, description=r.description, file_url=r.file_url, file_type=r.file_type, file_size=r.file_size, category=r.category, download_count=r.download_count, created_at=r.created_at, ) ) return PageResponse( items=items, total=total, page=page, page_size=page_size, total_pages=total_pages ) @router.post("/", response_model=ResourceOut) async def upload_new_resource( file: UploadFile = File(...), title: str = Form(...), category: str = Form(...), description: str | None = Form(None), class_id: int | None = Form(None), user: User = Depends(require_role("super_admin", "teacher", "student")), db: AsyncSession = Depends(get_db), ): effective_class_id = resolve_class_id_for_user(user, class_id) if effective_class_id is None: raise HTTPException(status_code=400, detail="You are not assigned to a class") ensure_class_permission(user, "resource_manage", effective_class_id) contents = await file.read() if len(contents) > 50 * 1024 * 1024: # 50MB limit raise HTTPException(status_code=400, detail="File too large (max 50MB)") if file.content_type not in ALLOWED_FILE_TYPES: raise HTTPException(status_code=400, detail=f"File type {file.content_type} not allowed") file_url = upload_file( f"resources/{effective_class_id}", file.filename or "file", contents, file.content_type, ) data = ResourceCreate(title=title, description=description, category=category) resource = await create_resource( db, effective_class_id, user.id, data, file_url, file.content_type, len(contents) ) return ResourceOut( id=resource.id, class_id=resource.class_id, uploader_id=resource.uploader_id, uploader_name=user.name, title=resource.title, description=resource.description, file_url=resource.file_url, file_type=resource.file_type, file_size=resource.file_size, category=resource.category, download_count=resource.download_count, created_at=resource.created_at, ) @router.post("/{resource_id}/download") async def download_resource( resource_id: int, user: User = Depends(require_role("super_admin", "teacher", "student")), db: AsyncSession = Depends(get_db), ): resource = await get_resource_by_id(db, resource_id) if resource is None: raise HTTPException(status_code=404, detail="Resource not found") ensure_class_access(user, resource.class_id) await increment_download_count(db, resource) return {"file_url": resource.file_url} @router.delete("/{resource_id}") async def delete_existing_resource( resource_id: int, user: User = Depends(require_role("super_admin", "teacher", "student")), db: AsyncSession = Depends(get_db), ): resource = await get_resource_by_id(db, resource_id) if resource is None: raise HTTPException(status_code=404, detail="Resource not found") ensure_class_permission(user, "resource_manage", resource.class_id) await delete_resource(db, resource) return {"message": "Resource deleted"}