160 lines
5.6 KiB
Python
160 lines
5.6 KiB
Python
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_module_enabled, 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)
|
|
await ensure_class_module_enabled(db, effective_class_id, "resources")
|
|
|
|
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")
|
|
await ensure_class_module_enabled(db, effective_class_id, "resources")
|
|
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 ensure_class_module_enabled(db, resource.class_id, "resources")
|
|
|
|
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")
|
|
await ensure_class_module_enabled(db, resource.class_id, "resources")
|
|
ensure_class_permission(user, "resource_manage", resource.class_id)
|
|
|
|
await delete_resource(db, resource)
|
|
return {"message": "Resource deleted"}
|