chore(pm): vendor the Aramesh/PM platform under pm/ (squash of Aramesh/PM @ 91f3271)
The PM panel (PmPanelPage -> iframe /pm/) needs the project-management platform served at /pm/ on the ERP domain. Source: org repo Aramesh/PM, branch feature/project-management-platform. Update via: git subtree pull --prefix=pm aramesh-pm feature/project-management-platform --squash
This commit is contained in:
@@ -0,0 +1,54 @@
|
||||
"""Defect photos — upload to PM volume, serve (LAN-internal, unauthenticated)."""
|
||||
import uuid
|
||||
|
||||
from fastapi import APIRouter, Depends, UploadFile
|
||||
from fastapi.responses import FileResponse
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from app.authz import AuthContext, Perm, require_permission
|
||||
from app.config import settings
|
||||
from app.db import get_db
|
||||
from app.errors import ValidationError
|
||||
|
||||
router = APIRouter(prefix="/files", tags=["files"])
|
||||
|
||||
ALLOWED = {"image/jpeg": ".jpg", "image/png": ".png", "image/webp": ".webp"}
|
||||
MAX_SIZE = 10 * 1024 * 1024
|
||||
|
||||
# photos are attached to QC defects, so both roles may upload
|
||||
upload_file = require_permission(Perm.files_upload)
|
||||
|
||||
|
||||
@router.post("")
|
||||
async def upload(
|
||||
file: UploadFile,
|
||||
_: AuthContext = Depends(upload_file),
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
_ = db
|
||||
ext = ALLOWED.get(file.content_type or "")
|
||||
if ext is None:
|
||||
raise ValidationError("فقط تصویر jpg/png/webp پذیرفته میشود", field="file")
|
||||
data = await file.read()
|
||||
if len(data) > MAX_SIZE:
|
||||
raise ValidationError("حجم فایل بیش از حد مجاز است (۱۰ مگابایت)", field="file")
|
||||
settings.FILES_DIR.mkdir(parents=True, exist_ok=True)
|
||||
file_id = uuid.uuid4().hex
|
||||
path = settings.FILES_DIR / f"{file_id}{ext}"
|
||||
path.write_bytes(data)
|
||||
return {"data": {"id": file_id, "url": f"/api/v1/files/{file_id}", "filename": file.filename}}
|
||||
|
||||
|
||||
@router.get("/{file_id}")
|
||||
async def serve(file_id: str, db: AsyncSession = Depends(get_db)):
|
||||
_ = db
|
||||
if not file_id.isalnum():
|
||||
raise ValidationError("شناسه فایل نامعتبر است")
|
||||
for ext in ALLOWED.values():
|
||||
path = settings.FILES_DIR / f"{file_id}{ext}"
|
||||
if path.exists():
|
||||
media = next(k for k, v in ALLOWED.items() if v == ext)
|
||||
return FileResponse(path, media_type=media)
|
||||
from app.errors import NotFoundError
|
||||
|
||||
raise NotFoundError("فایل یافت نشد")
|
||||
Reference in New Issue
Block a user