mirror of
https://github.com/BEDOLAGA-DEV/remnawave-bedolaga-telegram-bot.git
synced 2026-02-26 22:31:44 +00:00
Merge pull request #504 from Fr1ngg/bedolaga/-docs
Enable API key auth in web API docs
This commit is contained in:
@@ -28,6 +28,7 @@ def create_web_api_app() -> FastAPI:
|
||||
docs_url=docs_config.get("docs_url"),
|
||||
redoc_url=docs_config.get("redoc_url"),
|
||||
openapi_url=docs_config.get("openapi_url"),
|
||||
swagger_ui_parameters={"persistAuthorization": True},
|
||||
)
|
||||
|
||||
allowed_origins = settings.get_web_api_allowed_origins()
|
||||
|
||||
@@ -2,8 +2,8 @@ from __future__ import annotations
|
||||
|
||||
from typing import AsyncGenerator
|
||||
|
||||
from fastapi import Depends, HTTPException, Request, status
|
||||
from fastapi.security.utils import get_authorization_scheme_param
|
||||
from fastapi import Depends, HTTPException, Request, Security, status
|
||||
from fastapi.security import APIKeyHeader
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from app.database.database import AsyncSessionLocal
|
||||
@@ -11,6 +11,9 @@ from app.database.models import WebApiToken
|
||||
from app.services.web_api_token_service import web_api_token_service
|
||||
|
||||
|
||||
api_key_header_scheme = APIKeyHeader(name="X-API-Key", auto_error=False)
|
||||
|
||||
|
||||
async def get_db_session() -> AsyncGenerator[AsyncSession, None]:
|
||||
async with AsyncSessionLocal() as session:
|
||||
try:
|
||||
@@ -21,15 +24,17 @@ async def get_db_session() -> AsyncGenerator[AsyncSession, None]:
|
||||
|
||||
async def require_api_token(
|
||||
request: Request,
|
||||
api_key_header: str | None = Security(api_key_header_scheme),
|
||||
db: AsyncSession = Depends(get_db_session),
|
||||
) -> WebApiToken:
|
||||
api_key = request.headers.get("X-API-Key")
|
||||
api_key = api_key_header
|
||||
|
||||
if not api_key:
|
||||
authorization = request.headers.get("Authorization")
|
||||
scheme, param = get_authorization_scheme_param(authorization)
|
||||
if scheme.lower() == "bearer" and param:
|
||||
api_key = param
|
||||
if authorization:
|
||||
scheme, _, credentials = authorization.partition(" ")
|
||||
if scheme.lower() == "bearer" and credentials:
|
||||
api_key = credentials
|
||||
|
||||
if not api_key:
|
||||
raise HTTPException(
|
||||
|
||||
@@ -2,7 +2,7 @@ from __future__ import annotations
|
||||
|
||||
from typing import Any, Optional
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query, status
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query, Security, status
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from app.services.system_settings_service import bot_configuration_service
|
||||
@@ -100,7 +100,7 @@ def _serialize_definition(definition, include_choices: bool = True) -> SettingDe
|
||||
|
||||
@router.get("/categories", response_model=list[SettingCategorySummary])
|
||||
async def list_categories(
|
||||
_: object = Depends(require_api_token),
|
||||
_: object = Security(require_api_token),
|
||||
) -> list[SettingCategorySummary]:
|
||||
categories = bot_configuration_service.get_categories()
|
||||
return [
|
||||
@@ -111,7 +111,7 @@ async def list_categories(
|
||||
|
||||
@router.get("", response_model=list[SettingDefinition])
|
||||
async def list_settings(
|
||||
_: object = Depends(require_api_token),
|
||||
_: object = Security(require_api_token),
|
||||
category: Optional[str] = Query(default=None, alias="category_key"),
|
||||
) -> list[SettingDefinition]:
|
||||
items: list[SettingDefinition] = []
|
||||
@@ -130,7 +130,7 @@ async def list_settings(
|
||||
@router.get("/{key}", response_model=SettingDefinition)
|
||||
async def get_setting(
|
||||
key: str,
|
||||
_: object = Depends(require_api_token),
|
||||
_: object = Security(require_api_token),
|
||||
) -> SettingDefinition:
|
||||
try:
|
||||
definition = bot_configuration_service.get_definition(key)
|
||||
@@ -144,7 +144,7 @@ async def get_setting(
|
||||
async def update_setting(
|
||||
key: str,
|
||||
payload: SettingUpdateRequest,
|
||||
_: object = Depends(require_api_token),
|
||||
_: object = Security(require_api_token),
|
||||
db: AsyncSession = Depends(get_db_session),
|
||||
) -> SettingDefinition:
|
||||
try:
|
||||
@@ -162,7 +162,7 @@ async def update_setting(
|
||||
@router.delete("/{key}", response_model=SettingDefinition)
|
||||
async def reset_setting(
|
||||
key: str,
|
||||
_: object = Depends(require_api_token),
|
||||
_: object = Security(require_api_token),
|
||||
db: AsyncSession = Depends(get_db_session),
|
||||
) -> SettingDefinition:
|
||||
try:
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from fastapi import APIRouter, Depends
|
||||
from fastapi import APIRouter, Security
|
||||
|
||||
from app.config import settings
|
||||
from app.services.version_service import version_service
|
||||
@@ -12,7 +12,7 @@ router = APIRouter()
|
||||
|
||||
|
||||
@router.get("/health", tags=["health"], response_model=HealthCheckResponse)
|
||||
async def health_check(_: object = Depends(require_api_token)) -> HealthCheckResponse:
|
||||
async def health_check(_: object = Security(require_api_token)) -> HealthCheckResponse:
|
||||
return HealthCheckResponse(
|
||||
status="ok",
|
||||
api_version=settings.WEB_API_VERSION,
|
||||
|
||||
@@ -2,7 +2,7 @@ from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, Response, status
|
||||
from fastapi import APIRouter, Depends, HTTPException, Response, Security, status
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from app.database.crud.promo_group import (
|
||||
@@ -56,7 +56,7 @@ def _serialize(group: PromoGroup, members_count: int = 0) -> PromoGroupResponse:
|
||||
|
||||
@router.get("", response_model=list[PromoGroupResponse])
|
||||
async def list_promo_groups(
|
||||
_: Any = Depends(require_api_token),
|
||||
_: Any = Security(require_api_token),
|
||||
db: AsyncSession = Depends(get_db_session),
|
||||
) -> list[PromoGroupResponse]:
|
||||
groups_with_counts = await get_promo_groups_with_counts(db)
|
||||
@@ -66,7 +66,7 @@ async def list_promo_groups(
|
||||
@router.get("/{group_id}", response_model=PromoGroupResponse)
|
||||
async def get_promo_group(
|
||||
group_id: int,
|
||||
_: Any = Depends(require_api_token),
|
||||
_: Any = Security(require_api_token),
|
||||
db: AsyncSession = Depends(get_db_session),
|
||||
) -> PromoGroupResponse:
|
||||
group = await get_promo_group_by_id(db, group_id)
|
||||
@@ -80,7 +80,7 @@ async def get_promo_group(
|
||||
@router.post("", response_model=PromoGroupResponse, status_code=status.HTTP_201_CREATED)
|
||||
async def create_promo_group_endpoint(
|
||||
payload: PromoGroupCreateRequest,
|
||||
_: Any = Depends(require_api_token),
|
||||
_: Any = Security(require_api_token),
|
||||
db: AsyncSession = Depends(get_db_session),
|
||||
) -> PromoGroupResponse:
|
||||
group = await create_promo_group(
|
||||
@@ -100,7 +100,7 @@ async def create_promo_group_endpoint(
|
||||
async def update_promo_group_endpoint(
|
||||
group_id: int,
|
||||
payload: PromoGroupUpdateRequest,
|
||||
_: Any = Depends(require_api_token),
|
||||
_: Any = Security(require_api_token),
|
||||
db: AsyncSession = Depends(get_db_session),
|
||||
) -> PromoGroupResponse:
|
||||
group = await get_promo_group_by_id(db, group_id)
|
||||
@@ -125,7 +125,7 @@ async def update_promo_group_endpoint(
|
||||
@router.delete("/{group_id}", status_code=status.HTTP_204_NO_CONTENT)
|
||||
async def delete_promo_group_endpoint(
|
||||
group_id: int,
|
||||
_: Any = Depends(require_api_token),
|
||||
_: Any = Security(require_api_token),
|
||||
db: AsyncSession = Depends(get_db_session),
|
||||
) -> Response:
|
||||
group = await get_promo_group_by_id(db, group_id)
|
||||
|
||||
@@ -2,7 +2,7 @@ from __future__ import annotations
|
||||
|
||||
from datetime import datetime
|
||||
|
||||
from fastapi import APIRouter, Depends
|
||||
from fastapi import APIRouter, Depends, Security
|
||||
from sqlalchemy import func, select
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
@@ -24,7 +24,7 @@ router = APIRouter()
|
||||
|
||||
@router.get("/overview")
|
||||
async def stats_overview(
|
||||
_: object = Depends(require_api_token),
|
||||
_: object = Security(require_api_token),
|
||||
db: AsyncSession = Depends(get_db_session),
|
||||
) -> dict[str, object]:
|
||||
total_users = await db.scalar(select(func.count()).select_from(User)) or 0
|
||||
|
||||
@@ -2,7 +2,7 @@ from __future__ import annotations
|
||||
|
||||
from typing import Any, Optional
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query, status
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query, Security, status
|
||||
from sqlalchemy import select
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from sqlalchemy.orm import selectinload
|
||||
@@ -69,7 +69,7 @@ async def _get_subscription(db: AsyncSession, subscription_id: int) -> Subscript
|
||||
|
||||
@router.get("", response_model=list[SubscriptionResponse])
|
||||
async def list_subscriptions(
|
||||
_: Any = Depends(require_api_token),
|
||||
_: Any = Security(require_api_token),
|
||||
db: AsyncSession = Depends(get_db_session),
|
||||
limit: int = Query(50, ge=1, le=200),
|
||||
offset: int = Query(0, ge=0),
|
||||
@@ -95,7 +95,7 @@ async def list_subscriptions(
|
||||
@router.get("/{subscription_id}", response_model=SubscriptionResponse)
|
||||
async def get_subscription(
|
||||
subscription_id: int,
|
||||
_: Any = Depends(require_api_token),
|
||||
_: Any = Security(require_api_token),
|
||||
db: AsyncSession = Depends(get_db_session),
|
||||
) -> SubscriptionResponse:
|
||||
subscription = await _get_subscription(db, subscription_id)
|
||||
@@ -105,7 +105,7 @@ async def get_subscription(
|
||||
@router.post("", response_model=SubscriptionResponse, status_code=status.HTTP_201_CREATED)
|
||||
async def create_subscription(
|
||||
payload: SubscriptionCreateRequest,
|
||||
_: Any = Depends(require_api_token),
|
||||
_: Any = Security(require_api_token),
|
||||
db: AsyncSession = Depends(get_db_session),
|
||||
) -> SubscriptionResponse:
|
||||
existing = await get_subscription_by_user_id(db, payload.user_id)
|
||||
@@ -141,7 +141,7 @@ async def create_subscription(
|
||||
async def extend_subscription_endpoint(
|
||||
subscription_id: int,
|
||||
payload: SubscriptionExtendRequest,
|
||||
_: Any = Depends(require_api_token),
|
||||
_: Any = Security(require_api_token),
|
||||
db: AsyncSession = Depends(get_db_session),
|
||||
) -> SubscriptionResponse:
|
||||
subscription = await _get_subscription(db, subscription_id)
|
||||
@@ -154,7 +154,7 @@ async def extend_subscription_endpoint(
|
||||
async def add_subscription_traffic_endpoint(
|
||||
subscription_id: int,
|
||||
payload: SubscriptionTrafficRequest,
|
||||
_: Any = Depends(require_api_token),
|
||||
_: Any = Security(require_api_token),
|
||||
db: AsyncSession = Depends(get_db_session),
|
||||
) -> SubscriptionResponse:
|
||||
subscription = await _get_subscription(db, subscription_id)
|
||||
@@ -167,7 +167,7 @@ async def add_subscription_traffic_endpoint(
|
||||
async def add_subscription_devices_endpoint(
|
||||
subscription_id: int,
|
||||
payload: SubscriptionDevicesRequest,
|
||||
_: Any = Depends(require_api_token),
|
||||
_: Any = Security(require_api_token),
|
||||
db: AsyncSession = Depends(get_db_session),
|
||||
) -> SubscriptionResponse:
|
||||
subscription = await _get_subscription(db, subscription_id)
|
||||
@@ -180,7 +180,7 @@ async def add_subscription_devices_endpoint(
|
||||
async def add_subscription_squad_endpoint(
|
||||
subscription_id: int,
|
||||
payload: SubscriptionSquadRequest,
|
||||
_: Any = Depends(require_api_token),
|
||||
_: Any = Security(require_api_token),
|
||||
db: AsyncSession = Depends(get_db_session),
|
||||
) -> SubscriptionResponse:
|
||||
if not payload.squad_uuid:
|
||||
@@ -196,7 +196,7 @@ async def add_subscription_squad_endpoint(
|
||||
async def remove_subscription_squad_endpoint(
|
||||
subscription_id: int,
|
||||
squad_uuid: str,
|
||||
_: Any = Depends(require_api_token),
|
||||
_: Any = Security(require_api_token),
|
||||
db: AsyncSession = Depends(get_db_session),
|
||||
) -> SubscriptionResponse:
|
||||
subscription = await _get_subscription(db, subscription_id)
|
||||
|
||||
@@ -3,7 +3,7 @@ from __future__ import annotations
|
||||
from datetime import datetime
|
||||
from typing import Any, Optional
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query, status
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query, Security, status
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from app.database.crud.ticket import TicketCRUD
|
||||
@@ -56,7 +56,7 @@ def _serialize_ticket(ticket: Ticket, include_messages: bool = False) -> TicketR
|
||||
|
||||
@router.get("", response_model=list[TicketResponse])
|
||||
async def list_tickets(
|
||||
_: Any = Depends(require_api_token),
|
||||
_: Any = Security(require_api_token),
|
||||
db: AsyncSession = Depends(get_db_session),
|
||||
limit: int = Query(50, ge=1, le=200),
|
||||
offset: int = Query(0, ge=0),
|
||||
@@ -89,7 +89,7 @@ async def list_tickets(
|
||||
@router.get("/{ticket_id}", response_model=TicketResponse)
|
||||
async def get_ticket(
|
||||
ticket_id: int,
|
||||
_: Any = Depends(require_api_token),
|
||||
_: Any = Security(require_api_token),
|
||||
db: AsyncSession = Depends(get_db_session),
|
||||
) -> TicketResponse:
|
||||
ticket = await TicketCRUD.get_ticket_by_id(db, ticket_id, load_messages=True, load_user=False)
|
||||
@@ -102,7 +102,7 @@ async def get_ticket(
|
||||
async def update_ticket_status(
|
||||
ticket_id: int,
|
||||
payload: TicketStatusUpdateRequest,
|
||||
_: Any = Depends(require_api_token),
|
||||
_: Any = Security(require_api_token),
|
||||
db: AsyncSession = Depends(get_db_session),
|
||||
) -> TicketResponse:
|
||||
try:
|
||||
@@ -123,7 +123,7 @@ async def update_ticket_status(
|
||||
async def update_ticket_priority(
|
||||
ticket_id: int,
|
||||
payload: TicketPriorityUpdateRequest,
|
||||
_: Any = Depends(require_api_token),
|
||||
_: Any = Security(require_api_token),
|
||||
db: AsyncSession = Depends(get_db_session),
|
||||
) -> TicketResponse:
|
||||
allowed_priorities = {"low", "normal", "high", "urgent"}
|
||||
@@ -146,7 +146,7 @@ async def update_ticket_priority(
|
||||
async def update_reply_block(
|
||||
ticket_id: int,
|
||||
payload: TicketReplyBlockRequest,
|
||||
_: Any = Depends(require_api_token),
|
||||
_: Any = Security(require_api_token),
|
||||
db: AsyncSession = Depends(get_db_session),
|
||||
) -> TicketResponse:
|
||||
until = payload.until
|
||||
@@ -169,7 +169,7 @@ async def update_reply_block(
|
||||
@router.delete("/{ticket_id}/reply-block", response_model=TicketResponse)
|
||||
async def clear_reply_block(
|
||||
ticket_id: int,
|
||||
_: Any = Depends(require_api_token),
|
||||
_: Any = Security(require_api_token),
|
||||
db: AsyncSession = Depends(get_db_session),
|
||||
) -> TicketResponse:
|
||||
success = await TicketCRUD.set_user_reply_block(
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, Response, status
|
||||
from fastapi import APIRouter, Depends, HTTPException, Response, Security, status
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from app.database.crud.web_api_token import (
|
||||
@@ -35,7 +35,7 @@ def _serialize(token: WebApiToken) -> TokenResponse:
|
||||
|
||||
@router.get("", response_model=list[TokenResponse])
|
||||
async def get_tokens(
|
||||
_: WebApiToken = Depends(require_api_token),
|
||||
_: WebApiToken = Security(require_api_token),
|
||||
db: AsyncSession = Depends(get_db_session),
|
||||
) -> list[TokenResponse]:
|
||||
tokens = await list_tokens(db, include_inactive=True)
|
||||
@@ -45,7 +45,7 @@ async def get_tokens(
|
||||
@router.post("", response_model=TokenCreateResponse, status_code=status.HTTP_201_CREATED)
|
||||
async def create_token(
|
||||
payload: TokenCreateRequest,
|
||||
actor: WebApiToken = Depends(require_api_token),
|
||||
actor: WebApiToken = Security(require_api_token),
|
||||
db: AsyncSession = Depends(get_db_session),
|
||||
) -> TokenCreateResponse:
|
||||
token_value, token = await web_api_token_service.create_token(
|
||||
@@ -65,7 +65,7 @@ async def create_token(
|
||||
@router.post("/{token_id}/revoke", response_model=TokenResponse)
|
||||
async def revoke_token(
|
||||
token_id: int,
|
||||
_: WebApiToken = Depends(require_api_token),
|
||||
_: WebApiToken = Security(require_api_token),
|
||||
db: AsyncSession = Depends(get_db_session),
|
||||
) -> TokenResponse:
|
||||
token = await get_token_by_id(db, token_id)
|
||||
@@ -80,7 +80,7 @@ async def revoke_token(
|
||||
@router.post("/{token_id}/activate", response_model=TokenResponse)
|
||||
async def activate_token(
|
||||
token_id: int,
|
||||
_: WebApiToken = Depends(require_api_token),
|
||||
_: WebApiToken = Security(require_api_token),
|
||||
db: AsyncSession = Depends(get_db_session),
|
||||
) -> TokenResponse:
|
||||
token = await get_token_by_id(db, token_id)
|
||||
@@ -95,7 +95,7 @@ async def activate_token(
|
||||
@router.delete("/{token_id}", status_code=status.HTTP_204_NO_CONTENT)
|
||||
async def delete_token_endpoint(
|
||||
token_id: int,
|
||||
_: WebApiToken = Depends(require_api_token),
|
||||
_: WebApiToken = Security(require_api_token),
|
||||
db: AsyncSession = Depends(get_db_session),
|
||||
) -> Response:
|
||||
token = await get_token_by_id(db, token_id)
|
||||
|
||||
@@ -3,7 +3,7 @@ from __future__ import annotations
|
||||
from datetime import datetime
|
||||
from typing import Any, Optional
|
||||
|
||||
from fastapi import APIRouter, Depends, Query
|
||||
from fastapi import APIRouter, Depends, Query, Security
|
||||
from sqlalchemy import and_, func, select
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
@@ -33,7 +33,7 @@ def _serialize(transaction: Transaction) -> TransactionResponse:
|
||||
|
||||
@router.get("", response_model=TransactionListResponse)
|
||||
async def list_transactions(
|
||||
_: Any = Depends(require_api_token),
|
||||
_: Any = Security(require_api_token),
|
||||
db: AsyncSession = Depends(get_db_session),
|
||||
limit: int = Query(50, ge=1, le=200),
|
||||
offset: int = Query(0, ge=0),
|
||||
|
||||
@@ -2,7 +2,7 @@ from __future__ import annotations
|
||||
|
||||
from typing import Any, Optional
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query, status
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query, Security, status
|
||||
from sqlalchemy import func, or_, select
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from sqlalchemy.orm import selectinload
|
||||
@@ -111,7 +111,7 @@ def _apply_search_filter(query, search: str):
|
||||
|
||||
@router.get("", response_model=UserListResponse)
|
||||
async def list_users(
|
||||
_: Any = Depends(require_api_token),
|
||||
_: Any = Security(require_api_token),
|
||||
db: AsyncSession = Depends(get_db_session),
|
||||
limit: int = Query(50, ge=1, le=200),
|
||||
offset: int = Query(0, ge=0),
|
||||
@@ -155,7 +155,7 @@ async def list_users(
|
||||
@router.get("/{user_id}", response_model=UserResponse)
|
||||
async def get_user(
|
||||
user_id: int,
|
||||
_: Any = Depends(require_api_token),
|
||||
_: Any = Security(require_api_token),
|
||||
db: AsyncSession = Depends(get_db_session),
|
||||
) -> UserResponse:
|
||||
user = await get_user_by_id(db, user_id)
|
||||
@@ -168,7 +168,7 @@ async def get_user(
|
||||
@router.post("", response_model=UserResponse, status_code=status.HTTP_201_CREATED)
|
||||
async def create_user_endpoint(
|
||||
payload: UserCreateRequest,
|
||||
_: Any = Depends(require_api_token),
|
||||
_: Any = Security(require_api_token),
|
||||
db: AsyncSession = Depends(get_db_session),
|
||||
) -> UserResponse:
|
||||
existing = await get_user_by_telegram_id(db, payload.telegram_id)
|
||||
@@ -199,7 +199,7 @@ async def create_user_endpoint(
|
||||
async def update_user_endpoint(
|
||||
user_id: int,
|
||||
payload: UserUpdateRequest,
|
||||
_: Any = Depends(require_api_token),
|
||||
_: Any = Security(require_api_token),
|
||||
db: AsyncSession = Depends(get_db_session),
|
||||
) -> UserResponse:
|
||||
user = await get_user_by_id(db, user_id)
|
||||
@@ -252,7 +252,7 @@ async def update_user_endpoint(
|
||||
async def update_balance(
|
||||
user_id: int,
|
||||
payload: BalanceUpdateRequest,
|
||||
_: Any = Depends(require_api_token),
|
||||
_: Any = Security(require_api_token),
|
||||
db: AsyncSession = Depends(get_db_session),
|
||||
) -> UserResponse:
|
||||
if payload.amount_kopeks == 0:
|
||||
|
||||
Reference in New Issue
Block a user