feat: Добавлено лого команды и filename
This commit is contained in:
parent
562166086e
commit
21bc00ee33
@ -1,10 +1,12 @@
|
||||
from typing import Optional
|
||||
|
||||
from fastapi import APIRouter, Depends
|
||||
from fastapi import APIRouter, Depends, UploadFile, File
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from starlette.responses import FileResponse
|
||||
|
||||
from app.database.session import get_db
|
||||
from app.domain.entities.team import TeamEntity
|
||||
from app.domain.entities.team_logo import TeamLogoEntity
|
||||
from app.infrastructure.dependencies import get_current_user, require_admin
|
||||
from app.infrastructure.teams_service import TeamsService
|
||||
|
||||
@ -97,3 +99,33 @@ async def delete_team(
|
||||
):
|
||||
teams_service = TeamsService(db)
|
||||
return await teams_service.delete_team(team_id)
|
||||
|
||||
|
||||
@router.get(
|
||||
"/{team_id}/file/",
|
||||
response_class=FileResponse,
|
||||
summary="Download logo file by team ID",
|
||||
description="Returns the image file for the given team ID",
|
||||
)
|
||||
async def download_photo_file(
|
||||
team_id: int,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
teams_service = TeamsService(db)
|
||||
return await teams_service.get_photo_file_by_team_id(team_id)
|
||||
|
||||
|
||||
@router.post(
|
||||
"/{team_id}/upload/",
|
||||
response_model=TeamLogoEntity,
|
||||
summary="Upload a new photo for a profile",
|
||||
description="Uploads a new photo file and associates it with the given profile ID",
|
||||
)
|
||||
async def upload_photo(
|
||||
team_id: int,
|
||||
file: UploadFile = File(...),
|
||||
db: AsyncSession = Depends(get_db),
|
||||
user=Depends(get_current_user),
|
||||
):
|
||||
teams_service = TeamsService(db)
|
||||
return await teams_service.upload_photo(team_id, file)
|
||||
|
||||
@ -0,0 +1,32 @@
|
||||
"""0007_добавил filename для лого команды
|
||||
|
||||
Revision ID: b6c6c906cd2b
|
||||
Revises: 61271acdd22f
|
||||
Create Date: 2025-06-03 14:01:32.752389
|
||||
|
||||
"""
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision: str = 'b6c6c906cd2b'
|
||||
down_revision: Union[str, None] = '61271acdd22f'
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
"""Upgrade schema."""
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.add_column('teams', sa.Column('logo_filename', sa.String(), nullable=True))
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
"""Downgrade schema."""
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_column('teams', 'logo_filename')
|
||||
# ### end Alembic commands ###
|
||||
@ -10,6 +10,7 @@ class TeamEntity(BaseModel):
|
||||
title: str
|
||||
description: Optional[str] = None
|
||||
logo: Optional[str] = None
|
||||
logo_filename: Optional[str] = None
|
||||
git_url: Optional[str] = None
|
||||
is_active: bool
|
||||
|
||||
|
||||
6
API/app/domain/entities/team_logo.py
Normal file
6
API/app/domain/entities/team_logo.py
Normal file
@ -0,0 +1,6 @@
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class TeamLogoEntity(BaseModel):
|
||||
filename: str
|
||||
file_path: str
|
||||
@ -10,6 +10,7 @@ class Team(AdvancedBaseModel):
|
||||
title = Column(VARCHAR(150), nullable=False)
|
||||
description = Column(VARCHAR(150))
|
||||
logo = Column(String)
|
||||
logo_filename = Column(String)
|
||||
git_url = Column(String)
|
||||
is_active = Column(Boolean, default=False, nullable=False, server_default='false')
|
||||
|
||||
|
||||
@ -1,10 +1,17 @@
|
||||
import os
|
||||
import uuid
|
||||
from typing import Optional, Any, Coroutine
|
||||
|
||||
from fastapi import HTTPException, status
|
||||
import aiofiles
|
||||
from fastapi import HTTPException, status, UploadFile
|
||||
from magic import magic
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from starlette.responses import FileResponse
|
||||
from werkzeug.utils import secure_filename
|
||||
|
||||
from app.application.teams_repository import TeamsRepository
|
||||
from app.domain.entities.team import TeamEntity
|
||||
from app.domain.entities.team_logo import TeamLogoEntity
|
||||
from app.domain.models import Team
|
||||
|
||||
|
||||
@ -75,6 +82,36 @@ class TeamsService:
|
||||
|
||||
return self.model_to_entity(result)
|
||||
|
||||
async def get_photo_file_by_team_id(self, team_id: int) -> FileResponse:
|
||||
team = await self.teams_repository.get_by_id(team_id)
|
||||
|
||||
if not team:
|
||||
raise HTTPException(404, "Команда с таким ID не найдена")
|
||||
|
||||
if not os.path.exists(team.logo):
|
||||
raise HTTPException(404, "Файл логотипа не найден")
|
||||
|
||||
return FileResponse(
|
||||
team.logo,
|
||||
media_type=self.get_media_type(team.logo),
|
||||
filename=team.logo_filename,
|
||||
)
|
||||
|
||||
async def upload_photo(self, team_id: int, file: UploadFile):
|
||||
team = await self.teams_repository.get_by_id(team_id)
|
||||
|
||||
self.validate_file_type(file)
|
||||
|
||||
team.logo = await self.save_file(file)
|
||||
team.logo_filename = self.generate_filename(file)
|
||||
|
||||
team = await self.teams_repository.update(team)
|
||||
|
||||
return TeamLogoEntity(
|
||||
filename=team.logo_filename,
|
||||
file_path=team.logo,
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def model_to_entity(team_model: Team) -> TeamEntity:
|
||||
return TeamEntity(
|
||||
@ -82,6 +119,7 @@ class TeamsService:
|
||||
title=team_model.title,
|
||||
description=team_model.description,
|
||||
logo=team_model.logo,
|
||||
logo_filename=team_model.logo_filename,
|
||||
git_url=team_model.git_url,
|
||||
is_active=team_model.is_active,
|
||||
)
|
||||
@ -92,6 +130,7 @@ class TeamsService:
|
||||
title=team_entity.title,
|
||||
description=team_entity.description,
|
||||
logo=team_entity.logo,
|
||||
logo_filename=team_entity.logo_filename,
|
||||
git_url=team_entity.git_url,
|
||||
is_active=team_entity.is_active,
|
||||
)
|
||||
@ -100,3 +139,31 @@ class TeamsService:
|
||||
team_model.id = team_entity.id
|
||||
|
||||
return team_model
|
||||
|
||||
async def save_file(self, file: UploadFile, upload_dir: str = "uploads/team_logos") -> str:
|
||||
os.makedirs(upload_dir, exist_ok=True)
|
||||
filename = self.generate_filename(file)
|
||||
file_path = os.path.join(upload_dir, filename)
|
||||
|
||||
async with aiofiles.open(file_path, 'wb') as out_file:
|
||||
content = await file.read()
|
||||
await out_file.write(content)
|
||||
return file_path
|
||||
|
||||
@staticmethod
|
||||
def validate_file_type(file: UploadFile):
|
||||
mime = magic.Magic(mime=True)
|
||||
file_type = mime.from_buffer(file.file.read(1024))
|
||||
file.file.seek(0)
|
||||
|
||||
if file_type not in ["image/jpeg", "image/png"]:
|
||||
raise HTTPException(400, "Invalid file type")
|
||||
|
||||
@staticmethod
|
||||
def generate_filename(file: UploadFile):
|
||||
return secure_filename(f"{uuid.uuid4()}_{file.filename}")
|
||||
|
||||
@staticmethod
|
||||
def get_media_type(filename: str) -> str:
|
||||
extension = filename.split('.')[-1].lower()
|
||||
return f"image/{extension}" if extension in ['jpeg', 'jpg', 'png'] else "application/octet-stream"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user