klg-asutk-app/backend/app/api/routes/attachments.py
Yuriy aa052763f6 Безопасность и качество: 8 исправлений + обновления
- .env.example: полный шаблон, защита секретов
- .gitignore: явное исключение .env.* и секретов
- layout.tsx: XSS — заменён dangerouslySetInnerHTML на next/script для SW
- ESLint: no-console error (allow warn/error), ignore scripts/
- scripts/remove-console-logs.js: очистка console.log без glob
- backend/routes/modules: README с планом рефакторинга крупных файлов
- SECURITY.md: гид по секретам, XSS, CORS, auth, линту
- .husky/pre-commit: запуск npm run lint

+ прочие правки приложения и бэкенда

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-14 21:29:16 +03:00

85 lines
3.2 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

from fastapi import APIRouter, Depends, File, HTTPException, UploadFile
from fastapi.responses import FileResponse
from sqlalchemy.orm import Session
import os
from app.api.deps import get_current_user
from app.api.helpers import audit
from app.db.session import get_db
from app.models import Attachment
from app.schemas.attachment import AttachmentOut
from app.services.storage import save_upload
router = APIRouter(tags=["attachments"])
@router.post("/attachments/{owner_kind}/{owner_id}", response_model=AttachmentOut)
async def upload_attachment(owner_kind: str, owner_id: str, file: UploadFile = File(...), db: Session = Depends(get_db), user=Depends(get_current_user)):
storage_path, filename = await save_upload(owner_kind, owner_id, file)
att = Attachment(
owner_kind=owner_kind,
owner_id=owner_id,
filename=filename,
content_type=file.content_type,
storage_path=storage_path,
uploaded_by_user_id=user.id,
)
db.add(att)
audit(db, user, "create", "attachment", description=f"Uploaded {filename} for {owner_kind}/{owner_id}")
db.commit()
db.refresh(att)
return att
@router.get("/attachments/{attachment_id}", response_model=AttachmentOut)
def get_attachment_meta(attachment_id: str, db: Session = Depends(get_db), user=Depends(get_current_user)):
att = db.query(Attachment).filter(Attachment.id == attachment_id).first()
if not att:
raise HTTPException(status_code=404, detail="Not found")
return att
@router.get("/attachments/{attachment_id}/download")
def download_attachment(attachment_id: str, db: Session = Depends(get_db), user=Depends(get_current_user)):
att = db.query(Attachment).filter(Attachment.id == attachment_id).first()
if not att:
raise HTTPException(status_code=404, detail="Not found")
return FileResponse(path=att.storage_path, filename=att.filename, media_type=att.content_type)
@router.delete("/attachments/{attachment_id}", status_code=204)
def delete_attachment(attachment_id: str, db: Session = Depends(get_db), user=Depends(get_current_user)):
att = db.query(Attachment).filter(Attachment.id == attachment_id).first()
if not att:
raise HTTPException(status_code=404, detail="Not found")
# Удаляем файл с диска
if os.path.exists(att.storage_path):
try:
os.remove(att.storage_path)
except Exception as e:
# Логируем ошибку, но продолжаем удаление записи из БД
print(f"Error deleting file {att.storage_path}: {e}")
# Удаляем запись из БД
audit(db, user, "delete", "attachment", attachment_id, description=f"Deleted {att.filename}")
db.delete(att)
db.commit()
return None
@router.get("/attachments/{owner_kind}/{owner_id}", response_model=list[AttachmentOut])
def list_attachments(
owner_kind: str,
owner_id: str,
db: Session = Depends(get_db),
user=Depends(get_current_user),
):
return (
db.query(Attachment)
.filter(Attachment.owner_kind == owner_kind)
.filter(Attachment.owner_id == owner_id)
.order_by(Attachment.created_at.desc())
.all()
)