connect / routes /users.py
habulaj's picture
Update routes/users.py
edd6ece verified
raw
history blame
4.57 kB
import os
import logging
import asyncio
import aiohttp
from fastapi import APIRouter, HTTPException, Header, Query
from functools import lru_cache
from typing import List, Dict, Any, Optional
router = APIRouter()
# Configuração do Supabase
SUPABASE_URL = "https://ussxqnifefkgkaumjann.supabase.co"
SUPABASE_KEY = os.getenv("SUPA_KEY")
if not SUPABASE_KEY:
raise ValueError("❌ SUPA_KEY não foi definido no ambiente!")
SUPABASE_HEADERS = {
"apikey": SUPABASE_KEY,
"Authorization": f"Bearer {SUPABASE_KEY}",
"Content-Type": "application/json"
}
# Configuração do logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
# Cache para reduzir chamadas repetidas
@lru_cache(maxsize=128)
def get_cached_admin_status(user_id: str) -> bool:
"""Obtém e armazena em cache se um usuário é admin"""
import requests
user_data_url = f"{SUPABASE_URL}/rest/v1/User?id=eq.{user_id}"
response = requests.get(user_data_url, headers=SUPABASE_HEADERS)
if response.status_code != 200 or not response.json():
return False
user_info = response.json()[0]
return user_info.get("is_admin", False)
async def verify_admin_token(user_token: str) -> str:
"""Verifica se o token pertence a um administrador de forma assíncrona"""
headers = {
"Authorization": f"Bearer {user_token}",
"apikey": SUPABASE_KEY,
"Content-Type": "application/json"
}
async with aiohttp.ClientSession() as session:
async with session.get(f"{SUPABASE_URL}/auth/v1/user", headers=headers) as response:
if response.status != 200:
raise HTTPException(status_code=401, detail="Token inválido ou expirado")
user_data = await response.json()
user_id = user_data.get("id")
if not user_id:
raise HTTPException(status_code=400, detail="ID do usuário não encontrado")
is_admin = await asyncio.to_thread(get_cached_admin_status, user_id)
if not is_admin:
raise HTTPException(status_code=403, detail="Acesso negado: privilégios de administrador necessários")
return user_id
async def get_recent_users(limit: int = 50, search: Optional[str] = None, page: int = 0) -> Dict[str, Any]:
"""Obtém os usuários mais recentes da plataforma, com filtro opcional por nome e paginação"""
try:
offset = page * limit
limit_plus_one = limit + 1
query = f"{SUPABASE_URL}/rest/v1/User?select=name,avatar&order=created_at.desc&limit={limit_plus_one}&offset={offset}"
if search:
search_term = search.replace("'", "''")
query += f"&name=ilike.*{search_term}*"
headers = SUPABASE_HEADERS.copy()
headers["Accept"] = "application/json; charset=utf-8"
async with aiohttp.ClientSession() as session:
async with session.get(query, headers=headers) as response:
if response.status != 200:
logger.error(f"❌ Erro ao obter usuários: {response.status}")
return {"users": [], "has_next_page": False}
text = await response.text(encoding='utf-8')
import json
users_data = json.loads(text)
has_next_page = len(users_data) > limit
users = users_data[:limit]
return {
"users": users,
"has_next_page": has_next_page
}
except Exception as e:
logger.error(f"❌ Erro ao obter usuários: {str(e)}")
return {"users": [], "has_next_page": False}
@router.get("/admin/users")
async def get_recent_users_endpoint(
user_token: str = Header(None, alias="User-key"),
limit: int = Query(50, ge=1, le=100),
page: int = Query(0, ge=0),
search: Optional[str] = Query(None)
):
"""
Endpoint para obter os usuários mais recentes da plataforma,
com suporte a busca por nome e paginação.
"""
try:
user_id = await verify_admin_token(user_token)
result = await get_recent_users(limit, search, page)
return {
"users": result["users"],
"count": len(result["users"]),
"has_next_page": result["has_next_page"]
}
except HTTPException as he:
raise he
except Exception as e:
logger.error(f"❌ Erro ao obter usuários: {str(e)}")
raise HTTPException(status_code=500, detail=str(e))