Coverage for src / links / routers.py: 96%
45 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-05-10 22:03 +0200
« prev ^ index » next coverage.py v7.13.5, created at 2026-05-10 22:03 +0200
1from fastapi import APIRouter, Depends, HTTPException, Query, status
2from fastapi.responses import RedirectResponse
3from sqlalchemy.ext.asyncio import AsyncSession
4from typing import Optional
6from src.database import get_db
7from src.auth.manager import current_user, current_optional_user
8from src.auth.models import User
9from . import crud, schemas
10from .exceptions import NotUniqueAliasError, AliasLengthError
12router = APIRouter(prefix="/links", tags=["links"])
15# 1. Создание ссылки (доступно всем)
16@router.post("/shorten", response_model=schemas.LinkResponse)
17async def create_short_link(
18 link_data: schemas.LinkCreate,
19 db: AsyncSession = Depends(get_db),
20 user: Optional[User] = Depends(current_optional_user)
21):
22 try:
23 return await crud.create_link(
24 db=db,
25 original_url=str(link_data.original_url),
26 custom_alias=link_data.custom_alias,
27 expire_at=link_data.expire_at,
28 user_id=user.id if user else None
29 )
30 except (NotUniqueAliasError, AliasLengthError) as e:
31 raise HTTPException(status.HTTP_400_BAD_REQUEST, detail=str(e))
34# 2. Редирект (доступно всем)
35@router.get("/{short_id}")
36async def redirect_link(
37 short_id: str,
38 db: AsyncSession = Depends(get_db)
39):
40 link = await crud.get_link_by_short_id(db, short_id)
41 if not link:
42 raise HTTPException(status.HTTP_404_NOT_FOUND, detail="Link not found")
43 await crud.increment_click_count(db, link)
44 return RedirectResponse(url=link.original_url)
47# 3. Статистика (только для авторизованных)
48@router.get("/{short_id}/stats", response_model=schemas.LinkStatsResponse)
49async def get_link_stats(
50 short_id: str,
51 db: AsyncSession = Depends(get_db),
52 user: User = Depends(current_user)
53):
54 link = await crud.get_link_by_short_id(db, short_id)
55 if not link or link.user_id != user.id:
56 raise HTTPException(status.HTTP_404_NOT_FOUND, detail="Link not found")
58 return {
59 "original_url": link.original_url,
60 "short_id": link.short_id,
61 "custom_alias": link.custom_alias,
62 "created_at": link.created_at,
63 "expire_at": link.expire_at,
64 "click_count": link.click_count,
65 }
68# 4. Поиск (только для авторизованных)
69@router.get("/search/", response_model=list[schemas.LinkResponse])
70async def search_links(
71 original_url: str = Query(..., min_length=3),
72 db: AsyncSession = Depends(get_db),
73 user: User = Depends(current_user)
74):
75 return await crud.search_links(db, original_url, user.id)
78# 5. Удаление (только для авторизованных)
79@router.delete("/{short_id}")
80async def delete_link(
81 short_id: str,
82 db: AsyncSession = Depends(get_db),
83 user: User = Depends(current_user)
84):
85 link = await crud.get_link_by_short_id(db, short_id)
86 if not link or link.user_id != user.id:
87 raise HTTPException(status.HTTP_404_NOT_FOUND, detail="Link not found")
89 await crud.delete_link(db, link)
90 return {"status": "success"}
93# 6. Обновление (только для авторизованных)
94@router.put("/{short_id}", response_model=schemas.LinkResponse)
95async def update_link(
96 short_id: str,
97 update_data: schemas.LinkUpdate,
98 db: AsyncSession = Depends(get_db),
99 user: User = Depends(current_user)
100):
101 link = await crud.get_link_by_short_id(db, short_id)
102 if not link or link.user_id != user.id:
103 raise HTTPException(status.HTTP_404_NOT_FOUND, detail="Link not found")
105 return await crud.update_link(db, link, str(update_data.original_url))