110 lines
3.8 KiB
Python
110 lines
3.8 KiB
Python
from fastapi import APIRouter,Depends,File,Form,HTTPException,Query,UploadFile
|
|
from fastapi.responses import JSONResponse
|
|
from pydantic import BaseModel
|
|
|
|
from s3.views import S3Storage
|
|
from users.permissions import PermissionTag,require_permission
|
|
from dotenv import load_dotenv
|
|
load_dotenv()
|
|
|
|
router=APIRouter()
|
|
|
|
|
|
class DeleteObjectBody(BaseModel):
|
|
key: str
|
|
|
|
|
|
@router.get("/s3/health")
|
|
async def s3_health():
|
|
"""Bucket reachability — unauthenticated like GET /sheet/health."""
|
|
try:
|
|
service=S3Storage()
|
|
data=await service.health_check()
|
|
return JSONResponse(content={"data":data,"total":1,"status_code":200})
|
|
except HTTPException:
|
|
raise
|
|
except Exception as e:
|
|
raise HTTPException(status_code=500,detail=str(e))
|
|
|
|
|
|
@router.post("/s3/upload")
|
|
async def upload_s3_file(
|
|
file: UploadFile=File(...),
|
|
source: str=Form(...),
|
|
record_id: str=Form(...),
|
|
owner_id: str=Form(...),
|
|
current_user: dict=Depends(require_permission(PermissionTag.CANDIDATES_CREATE,PermissionTag.SETTINGS_EDIT,require_all=False)),
|
|
):
|
|
"""PDF only. Private PutObject under {source}/{record_id}/{owner_id}/{file}.pdf."""
|
|
try:
|
|
service=S3Storage()
|
|
data=await service.upload_for_record(file,source=source,record_id=record_id,owner_id=owner_id)
|
|
return JSONResponse(content={"data":data,"total":1,"status_code":200})
|
|
except HTTPException:
|
|
raise
|
|
except Exception as e:
|
|
raise HTTPException(status_code=500,detail=str(e))
|
|
|
|
|
|
@router.get("/s3/url")
|
|
async def fetch_s3_url(
|
|
key: str=Query(...),
|
|
current_user: dict=Depends(require_permission(PermissionTag.CANDIDATES_VIEW,PermissionTag.SETTINGS_VIEW,require_all=False)),
|
|
):
|
|
"""Stable private object address stored in file_path (not anonymously openable)."""
|
|
try:
|
|
service=S3Storage()
|
|
data=await service.object_url(key)
|
|
return JSONResponse(content={"data":data,"total":1,"status_code":200})
|
|
except HTTPException:
|
|
raise
|
|
except Exception as e:
|
|
raise HTTPException(status_code=500,detail=str(e))
|
|
|
|
|
|
@router.get("/s3/open")
|
|
async def open_s3_file(
|
|
key: str=Query(...,description="S3 key or stored file_path URL"),
|
|
expires_in: int | None=Query(None,ge=60,le=604800),
|
|
current_user: dict=Depends(require_permission(PermissionTag.CANDIDATES_VIEW,PermissionTag.SETTINGS_VIEW,PermissionTag.INBOX_VIEW,require_all=False)),
|
|
):
|
|
"""Short-lived presigned GET for a private CV — open this URL in the browser."""
|
|
try:
|
|
service=S3Storage()
|
|
data=await service.open_url(key,expires_in=expires_in)
|
|
return JSONResponse(content={"data":data,"total":1,"status_code":200})
|
|
except HTTPException:
|
|
raise
|
|
except Exception as e:
|
|
raise HTTPException(status_code=500,detail=str(e))
|
|
|
|
|
|
@router.get("/s3/download")
|
|
async def download_s3_file(
|
|
key: str=Query(...,description="S3 key or stored file_path URL"),
|
|
current_user: dict=Depends(require_permission(PermissionTag.CANDIDATES_VIEW,PermissionTag.SETTINGS_VIEW,PermissionTag.INBOX_VIEW,require_all=False)),
|
|
):
|
|
"""Stream a private PDF through the API (IAM GetObject — no public bucket)."""
|
|
try:
|
|
service=S3Storage()
|
|
return await service.download_file(key)
|
|
except HTTPException:
|
|
raise
|
|
except Exception as e:
|
|
raise HTTPException(status_code=500,detail=str(e))
|
|
|
|
|
|
@router.post("/s3/delete")
|
|
async def delete_s3_file(
|
|
payload: DeleteObjectBody,
|
|
current_user: dict=Depends(require_permission(PermissionTag.CANDIDATES_DELETE,PermissionTag.SETTINGS_DELETE,require_all=False)),
|
|
):
|
|
try:
|
|
service=S3Storage()
|
|
data=await service.delete_file(payload.key)
|
|
return JSONResponse(content={"data":data,"total":1,"status_code":200})
|
|
except HTTPException:
|
|
raise
|
|
except Exception as e:
|
|
raise HTTPException(status_code=500,detail=str(e))
|