This commit is contained in:
xds
2026-02-04 15:10:55 +03:00
parent 11c1f4f7dc
commit 35de8efc56
20 changed files with 566 additions and 135 deletions

View File

@@ -1,92 +0,0 @@
from typing import List, Optional
from aiogram.types import BufferedInputFile
from fastapi import APIRouter, UploadFile, File, Form
from fastapi.openapi.models import MediaType
from starlette.exceptions import HTTPException
from starlette.requests import Request
from starlette.responses import Response, JSONResponse
from models.Asset import Asset, AssetType
from repos.dao import DAO
router = APIRouter(prefix="/api/assets", tags=["Assets"])
@router.get("/{asset_id}")
async def get_asset(asset_id: str, request: Request) -> Response:
dao = request.app.state.dao
asset = await dao.assets.get_asset(asset_id)
# 2. Проверка на существование
if not asset:
raise HTTPException(status_code=404, detail="Asset not found")
headers = {
# Кэшировать на 1 год (31536000 сек)
"Cache-Control": "public, max-age=31536000, immutable"
}
return Response(content=asset.data, media_type="image/png", headers=headers)
@router.get("")
async def get_assets(request: Request) -> List[Asset]:
dao: DAO = request.app.state.dao
assets = await dao.assets.get_assets()
return assets
@router.post("/upload", response_model=Asset)
async def upload_asset(
request: Request,
# Файл обязателен
file: UploadFile = File(...),
# Остальные поля принимаем как Form-data (не JSON!)
name: str = Form(...),
type: AssetType = Form(...),
linked_char_id: Optional[str] = Form(None)
):
"""
Загружает файл, отправляет его в ТГ (для получения ID) и сохраняет в БД.
"""
# 1. Читаем байты файла
file_content = await file.read()
if not file_content:
raise HTTPException(status_code=400, detail="File is empty")
# 2. Получаем необходимые зависимости из state
bot = request.app.state.bot # Бот нужен, чтобы получить tg_file_id
admin_id = request.app.state.admin_id # Куда отправлять файл "на хранение"
dao = request.app.state.assets_dao
# 3. Отправляем файл в Telegram, чтобы получить tg_doc_file_id
# (Это обязательно, так как ваша модель требует этот ID)
try:
tg_msg = await bot.send_document(
chat_id=admin_id,
document=BufferedInputFile(file_content, filename=file.filename),
caption=f"📥 Uploaded via API: {name}"
)
# Получаем ID документа из ответа ТГ
tg_doc_id = tg_msg.document.file_id
# Если это картинка, можно попытаться достать и photo_id (для превью)
# Но send_document обычно возвращает именно документ.
except Exception as e:
raise HTTPException(status_code=500, detail=f"Failed to upload to Telegram: {e}")
# 4. Создаем объект Asset
# Pydantic сам подставит created_at и вычислит link
new_asset = Asset(
name=name,
type=type,
linked_char_id=linked_char_id,
data=file_content, # Сохраняем байты в БД
tg_doc_file_id=tg_doc_id # ID из телеграма
)
# 5. Сохраняем через DAO
saved_asset = await dao.save_asset(new_asset)
return saved_asset

View File

@@ -0,0 +1,74 @@
from typing import List, Optional
from aiogram.types import BufferedInputFile
from fastapi import APIRouter, UploadFile, File, Form, Depends
from fastapi.openapi.models import MediaType
from starlette import status
from starlette.exceptions import HTTPException
from starlette.requests import Request
from starlette.responses import Response, JSONResponse
from api.models.AssetDTO import AssetsResponse, AssetResponse
from models.Asset import Asset, AssetType
from repos.dao import DAO
from api.dependency import get_dao
router = APIRouter(prefix="/api/assets", tags=["Assets"])
@router.get("/{asset_id}")
async def get_asset(asset_id: str, request: Request,dao: DAO = Depends(get_dao),) -> Response:
asset = await dao.assets.get_asset(asset_id)
# 2. Проверка на существование
if not asset:
raise HTTPException(status_code=404, detail="Asset not found")
headers = {
# Кэшировать на 1 год (31536000 сек)
"Cache-Control": "public, max-age=31536000, immutable"
}
return Response(content=asset.data, media_type="image/png", headers=headers)
@router.get("")
async def get_assets(request: Request, dao: DAO = Depends(get_dao), limit: int = 10, offset: int = 0) -> AssetsResponse:
assets = await dao.assets.get_assets(limit, offset)
assets = await dao.assets.get_assets()
total_count = await dao.assets.get_asset_count()
return AssetsResponse(assets=assets, total_count=total_count)
@router.post("/upload", response_model=AssetResponse, status_code=status.HTTP_201_CREATED)
async def upload_asset(
file: UploadFile = File(...),
linked_char_id: Optional[str] = Form(None),
dao: DAO = Depends(get_dao),
):
if not file.content_type:
raise HTTPException(status_code=400, detail="Unknown file type")
if not file.content_type.startswith("image/"):
raise HTTPException(status_code=400, detail=f"Unsupported content type: {file.content_type}")
data = await file.read()
if not data:
raise HTTPException(status_code=400, detail="Empty file")
asset = Asset(
name=file.filename or "upload",
type=AssetType.IMAGE,
linked_char_id=linked_char_id,
data=data,
)
asset_id = await dao.assets.create_asset(asset)
asset.id = str(asset_id)
return AssetResponse(
id=asset.id,
name=asset.name,
type=asset.type.value if hasattr(asset.type, "value") else asset.type,
linked_char_id=asset.linked_char_id,
created_at=asset.created_at,
)

View File

@@ -1,35 +1,44 @@
from typing import List
from typing import List, Any, Coroutine
from fastapi import APIRouter
from fastapi import APIRouter, Depends
from pydantic import BaseModel
from starlette.exceptions import HTTPException
from starlette.requests import Request
from api.models.AssetDTO import AssetsResponse
from api.models.GenerationRequest import GenerationRequest, GenerationResponse
from models.Asset import Asset
from models.Character import Character
from repos.dao import DAO
from api.dependency import get_dao
router = APIRouter(prefix="/api/characters", tags=["Characters"])
@router.get("/", response_model=List[Character])
async def get_characters(request: Request) -> List[Character]:
dao: DAO = request.app.state.dao
async def get_characters(request: Request, dao: DAO = Depends(get_dao), ) -> List[Character]:
characters = await dao.chars.get_all_characters()
return characters
@router.get("/{character_id}/assets", response_model=List[Asset])
async def get_character_assets(character_id: str, request: Request) -> List[Asset]:
dao: DAO = request.app.state.dao
@router.get("/{character_id}/assets", response_model=AssetsResponse)
async def get_character_assets(character_id: str, dao: DAO = Depends(get_dao), limit: int = 10,
offset: int = 0, ) -> AssetsResponse:
character = await dao.chars.get_character(character_id)
if character is None:
raise HTTPException(status_code=404, detail="Character not found")
return await dao.assets.get_assets_by_char_id(character_id)
assets = await dao.assets.get_assets_by_char_id(character_id, limit, offset)
total_count = await dao.assets.get_asset_count(character_id)
return AssetsResponse(assets=assets, total_count=total_count)
@router.get("/{character_id}", response_model=Character)
async def get_character_by_id(character_id: str, request: Request) -> Character:
dao: DAO = request.app.state.dao
async def get_character_by_id(character_id: str, request: Request, dao: DAO = Depends(get_dao)) -> Character:
character = await dao.chars.get_character(character_id)
return character
@router.post("/{character_id}/_run", response_model=Asset)
async def post_character_generation(character_id: str, generation: GenerationRequest,
request: Request) -> GenerationResponse:
generation_service = request.app.state.generation_service

View File

@@ -0,0 +1,47 @@
from typing import List, Optional
from fastapi import APIRouter
from fastapi.params import Depends
from starlette.requests import Request
from api import service
from api.dependency import get_generation_service
from api.models.GenerationRequest import GenerationResponse, GenerationRequest, PromptResponse, PromptRequest
from api.service.generation_service import GenerationService
from models.Generation import Generation
router = APIRouter(prefix='/api/generations', tags=["Generation"])
@router.post("/prompt-assistant", response_model=PromptResponse)
async def ask_prompt_assistant(prompt_request: PromptRequest, request: Request,
generation_service: GenerationService = Depends(
get_generation_service)) -> PromptResponse:
generated_prompt = await generation_service.ask_prompt_assistant(prompt_request.prompt, prompt_request.linked_assets)
return PromptResponse(prompt=generated_prompt)
@router.get("", response_model=List[GenerationResponse])
async def get_generations(character_id: Optional[str], limit: int = 10, offset: int = 0,
generation_service: GenerationService = Depends(get_generation_service)):
return await generation_service.get_generations(character_id, limit=limit, offset=offset)
@router.post("/_run", response_model=GenerationResponse)
async def post_generation(generation: GenerationRequest, request: Request,
generation_service: GenerationService = Depends(
get_generation_service)) -> GenerationResponse:
return await generation_service.create_generation_task(generation)
@router.get("/{generation_id}", response_model=GenerationResponse)
async def get_generation(generation_id: str,
generation_service: GenerationService = Depends(get_generation_service)) -> GenerationResponse:
return await generation_service.get_generation(generation_id)
@router.get("/running")
async def get_running_generations(request: Request,
generation_service: GenerationService = Depends(get_generation_service)):
return await generation_service.get_running_generations()