habulaj commited on
Commit
c6f1c6d
·
verified ·
1 Parent(s): 7d1148e

Update routes/onboarding.py

Browse files
Files changed (1) hide show
  1. routes/onboarding.py +94 -63
routes/onboarding.py CHANGED
@@ -1,9 +1,12 @@
1
  import os
2
  import logging
3
  import aiohttp
 
4
  from pydantic import BaseModel
5
  from fastapi import APIRouter, HTTPException, Body, Query, Header
6
  from typing import List, Dict, Any, Optional, Union
 
 
7
 
8
  router = APIRouter()
9
 
@@ -45,66 +48,101 @@ SUPABASE_ROLE_HEADERS = {
45
  logging.basicConfig(level=logging.INFO)
46
  logger = logging.getLogger(__name__)
47
 
48
- async def verify_token_with_permissions(user_token: str, required_permission: Optional[str] = None) -> Dict[str, Any]:
49
- """Verifica o token e retorna ID do usuário e suas permissões"""
50
- headers = {
51
- "Authorization": f"Bearer {user_token}",
52
- "apikey": SUPABASE_KEY,
53
- "Content-Type": "application/json"
54
- }
55
 
56
- # Log para depuração
57
- logger.info("Iniciando verificação de token")
58
 
59
- try:
60
- async with aiohttp.ClientSession() as session:
61
- auth_url = f"{SUPABASE_URL}/auth/v1/user"
62
- logger.info(f"Enviando requisição para {auth_url}")
63
-
64
- async with session.get(auth_url, headers=headers) as response:
65
- status = response.status
66
- logger.info(f"Resposta da verificação do token: {status}")
67
-
68
- if status != 200:
69
- response_text = await response.text()
70
- logger.error(f"Erro na autenticação: {status} - {response_text}")
71
- raise HTTPException(status_code=401, detail=f"Token inválido ou expirado: {response_text}")
72
-
73
- user_data = await response.json()
74
- user_id = user_data.get("id")
75
- if not user_id:
76
- raise HTTPException(status_code=400, detail="ID do usuário não encontrado na resposta")
77
-
78
- # Log para depuração
79
- logger.info(f"Token válido para usuário {user_id}, verificando permissões")
80
-
81
- # Obter permissões do usuário
82
  user_data_url = f"{SUPABASE_URL}/rest/v1/User?id=eq.{user_id}&select=is_admin,edit_onboarding"
83
 
84
  async with aiohttp.ClientSession() as session:
85
  async with session.get(user_data_url, headers=SUPABASE_HEADERS) as response:
86
  if response.status != 200 or not await response.json():
87
- raise HTTPException(status_code=403, detail="Acesso negado: não foi possível verificar permissões")
88
 
89
  user_info = (await response.json())[0]
90
- is_admin = user_info.get("is_admin", False)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
91
 
92
- if not is_admin:
93
- raise HTTPException(status_code=403, detail="Acesso negado: privilégios de administrador necessários")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
94
 
95
- # Verificar permissão específica, se requisitada
96
- if required_permission:
97
- has_permission = user_info.get(required_permission, False)
98
- if not has_permission:
99
- raise HTTPException(
100
- status_code=403,
101
- detail=f"Acesso negado: permissão '{required_permission}' necessária"
102
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
103
 
104
  return {
105
  "user_id": user_id,
106
  "is_admin": is_admin,
107
- "permissions": user_info
108
  }
109
 
110
  @router.patch("/onboarding/update-question")
@@ -115,12 +153,11 @@ async def update_onboarding_question(
115
  """
116
  Atualiza uma pergunta de onboarding com base no ID.
117
  Apenas os campos enviados no payload serão atualizados.
118
- Requer permissão de admin e edit_onboarding=true.
119
  """
120
  try:
121
  # Verificar se o usuário é admin e tem permissão para editar onboarding
122
  await verify_token_with_permissions(user_token, "edit_onboarding")
123
-
124
  update_data = {}
125
 
126
  if payload.title is not None:
@@ -163,12 +200,11 @@ async def add_onboarding_question(
163
  """
164
  Adiciona uma nova pergunta de onboarding.
165
  Trata casos onde `options` vem como `{}` ou string.
166
- Requer permissão de admin e edit_onboarding=true.
167
  """
168
  try:
169
  # Verificar se o usuário é admin e tem permissão para editar onboarding
170
  await verify_token_with_permissions(user_token, "edit_onboarding")
171
-
172
  # Tratamento de `options`
173
  options = payload.options
174
 
@@ -213,12 +249,11 @@ async def delete_onboarding_question(
213
  ):
214
  """
215
  Deleta uma pergunta de onboarding com base no ID (passado via query parameter).
216
- Requer permissão de admin e edit_onboarding=true.
217
  """
218
  try:
219
  # Verificar se o usuário é admin e tem permissão para editar onboarding
220
  await verify_token_with_permissions(user_token, "edit_onboarding")
221
-
222
  query_url = f"{SUPABASE_URL}/rest/v1/Onboarding?id=eq.{id}"
223
  headers = SUPABASE_ROLE_HEADERS.copy()
224
  headers["Prefer"] = "return=representation"
@@ -243,24 +278,19 @@ async def delete_onboarding_question(
243
  raise HTTPException(status_code=500, detail="Erro interno do servidor")
244
 
245
  @router.get("/onboarding/questions")
246
- async def get_onboarding_questions(user_token: str = Header(None, alias="User-key")) -> Dict[str, List[Dict[str, Any]]]:
 
 
247
  """
248
  Retorna todas as perguntas de onboarding, separadas por target_type (client e stylist).
249
- Requer permissão de admin e edit_onboarding=true.
250
  """
251
  try:
252
- # Verificar se o token foi fornecido
253
- if not user_token:
254
- raise HTTPException(status_code=401, detail="Token de autenticação não fornecido")
255
-
256
- # Adicionar log para depuração
257
- logger.info(f"Verificando token para acesso ao endpoint /onboarding/questions")
258
-
259
  # Verificar se o usuário é admin e tem permissão para editar onboarding
260
- await verify_token_with_permissions(user_token, "edit_onboarding")
 
261
 
262
- # Se chegou aqui, o token é válido e o usuário tem permissões
263
- logger.info("Token validado com sucesso, buscando perguntas de onboarding")
264
 
265
  query_url = f"{SUPABASE_URL}/rest/v1/Onboarding?select=id,title,description,question_type,options,target_type,optional,lock&order=created_at.asc"
266
  headers = SUPABASE_HEADERS.copy()
@@ -300,6 +330,7 @@ async def get_onboarding_questions(user_token: str = Header(None, alias="User-ke
300
  }
301
 
302
  except HTTPException as he:
 
303
  raise he
304
  except Exception as e:
305
  logger.error(f"❌ Erro ao obter perguntas de onboarding: {str(e)}")
 
1
  import os
2
  import logging
3
  import aiohttp
4
+ import asyncio
5
  from pydantic import BaseModel
6
  from fastapi import APIRouter, HTTPException, Body, Query, Header
7
  from typing import List, Dict, Any, Optional, Union
8
+ from functools import lru_cache
9
+ import requests
10
 
11
  router = APIRouter()
12
 
 
48
  logging.basicConfig(level=logging.INFO)
49
  logger = logging.getLogger(__name__)
50
 
51
+ # Cache para reduzir chamadas repetidas
52
+ @lru_cache(maxsize=128)
53
+ def get_cached_admin_status(user_id: str) -> bool:
54
+ """Obtém e armazena em cache se um usuário é admin"""
55
+ user_data_url = f"{SUPABASE_URL}/rest/v1/User?id=eq.{user_id}"
56
+ response = requests.get(user_data_url, headers=SUPABASE_HEADERS)
 
57
 
58
+ if response.status_code != 200 or not response.json():
59
+ return False
60
 
61
+ user_info = response.json()[0]
62
+ return user_info.get("is_admin", False)
63
+
64
+ # Função para obter permissões do usuário
65
+ async def get_user_permissions(user_id: str) -> Dict[str, bool]:
66
+ """Obtém as permissões de um usuário"""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
67
  user_data_url = f"{SUPABASE_URL}/rest/v1/User?id=eq.{user_id}&select=is_admin,edit_onboarding"
68
 
69
  async with aiohttp.ClientSession() as session:
70
  async with session.get(user_data_url, headers=SUPABASE_HEADERS) as response:
71
  if response.status != 200 or not await response.json():
72
+ return {"is_admin": False, "edit_onboarding": False}
73
 
74
  user_info = (await response.json())[0]
75
+ return {
76
+ "is_admin": user_info.get("is_admin", False),
77
+ "edit_onboarding": user_info.get("edit_onboarding", False)
78
+ }
79
+
80
+ # Verificação de token admin (mantida para compatibilidade)
81
+ async def verify_admin_token(user_token: str) -> str:
82
+ """Verifica se o token pertence a um administrador de forma assíncrona"""
83
+ headers = {
84
+ "Authorization": f"Bearer {user_token}",
85
+ "apikey": SUPABASE_KEY,
86
+ "Content-Type": "application/json"
87
+ }
88
+
89
+ async with aiohttp.ClientSession() as session:
90
+ async with session.get(f"{SUPABASE_URL}/auth/v1/user", headers=headers) as response:
91
+ if response.status != 200:
92
+ raise HTTPException(status_code=401, detail="Token inválido ou expirado")
93
 
94
+ user_data = await response.json()
95
+ user_id = user_data.get("id")
96
+ if not user_id:
97
+ raise HTTPException(status_code=400, detail="ID do usuário não encontrado")
98
+
99
+ is_admin = await asyncio.to_thread(get_cached_admin_status, user_id)
100
+
101
+ if not is_admin:
102
+ raise HTTPException(status_code=403, detail="Acesso negado: privilégios de administrador necessários")
103
+
104
+ return user_id
105
+
106
+ # Verificação de token com permissões específicas
107
+ async def verify_token_with_permissions(user_token: str, required_permission: Optional[str] = None) -> Dict[str, Any]:
108
+ """Verifica o token e verifica se o usuário tem a permissão necessária"""
109
+ headers = {
110
+ "Authorization": f"Bearer {user_token}",
111
+ "apikey": SUPABASE_KEY,
112
+ "Content-Type": "application/json"
113
+ }
114
+
115
+ async with aiohttp.ClientSession() as session:
116
+ async with session.get(f"{SUPABASE_URL}/auth/v1/user", headers=headers) as response:
117
+ if response.status != 200:
118
+ raise HTTPException(status_code=401, detail="Token inválido ou expirado")
119
 
120
+ user_data = await response.json()
121
+ user_id = user_data.get("id")
122
+ if not user_id:
123
+ raise HTTPException(status_code=400, detail="ID do usuário não encontrado")
124
+
125
+ # Obter permissões do usuário
126
+ permissions = await get_user_permissions(user_id)
127
+
128
+ # Verificar se é admin
129
+ is_admin = permissions.get("is_admin", False)
130
+ if not is_admin:
131
+ raise HTTPException(status_code=403, detail="Acesso negado: privilégios de administrador necessários")
132
+
133
+ # Verificar permissão específica, se requisitada
134
+ if required_permission:
135
+ has_permission = permissions.get(required_permission, False)
136
+ if not has_permission:
137
+ raise HTTPException(
138
+ status_code=403,
139
+ detail=f"Acesso negado: permissão '{required_permission}' necessária"
140
+ )
141
 
142
  return {
143
  "user_id": user_id,
144
  "is_admin": is_admin,
145
+ "permissions": permissions
146
  }
147
 
148
  @router.patch("/onboarding/update-question")
 
153
  """
154
  Atualiza uma pergunta de onboarding com base no ID.
155
  Apenas os campos enviados no payload serão atualizados.
 
156
  """
157
  try:
158
  # Verificar se o usuário é admin e tem permissão para editar onboarding
159
  await verify_token_with_permissions(user_token, "edit_onboarding")
160
+
161
  update_data = {}
162
 
163
  if payload.title is not None:
 
200
  """
201
  Adiciona uma nova pergunta de onboarding.
202
  Trata casos onde `options` vem como `{}` ou string.
 
203
  """
204
  try:
205
  # Verificar se o usuário é admin e tem permissão para editar onboarding
206
  await verify_token_with_permissions(user_token, "edit_onboarding")
207
+
208
  # Tratamento de `options`
209
  options = payload.options
210
 
 
249
  ):
250
  """
251
  Deleta uma pergunta de onboarding com base no ID (passado via query parameter).
 
252
  """
253
  try:
254
  # Verificar se o usuário é admin e tem permissão para editar onboarding
255
  await verify_token_with_permissions(user_token, "edit_onboarding")
256
+
257
  query_url = f"{SUPABASE_URL}/rest/v1/Onboarding?id=eq.{id}"
258
  headers = SUPABASE_ROLE_HEADERS.copy()
259
  headers["Prefer"] = "return=representation"
 
278
  raise HTTPException(status_code=500, detail="Erro interno do servidor")
279
 
280
  @router.get("/onboarding/questions")
281
+ async def get_onboarding_questions(
282
+ user_token: str = Header(None, alias="User-key")
283
+ ) -> Dict[str, List[Dict[str, Any]]]:
284
  """
285
  Retorna todas as perguntas de onboarding, separadas por target_type (client e stylist).
 
286
  """
287
  try:
 
 
 
 
 
 
 
288
  # Verificar se o usuário é admin e tem permissão para editar onboarding
289
+ # Adicionamos um log para verificar o que está acontecendo
290
+ logger.info(f"Recebido token: {user_token[:10]}... para acesso às perguntas de onboarding")
291
 
292
+ await verify_token_with_permissions(user_token, "edit_onboarding")
293
+ logger.info("Token verificado com sucesso!")
294
 
295
  query_url = f"{SUPABASE_URL}/rest/v1/Onboarding?select=id,title,description,question_type,options,target_type,optional,lock&order=created_at.asc"
296
  headers = SUPABASE_HEADERS.copy()
 
330
  }
331
 
332
  except HTTPException as he:
333
+ logger.error(f"❌ HTTPException: {he.detail}")
334
  raise he
335
  except Exception as e:
336
  logger.error(f"❌ Erro ao obter perguntas de onboarding: {str(e)}")