habulaj commited on
Commit
b6ec3ee
·
verified ·
1 Parent(s): 2b66b9a

Update routes/sendnotifications.py

Browse files
Files changed (1) hide show
  1. routes/sendnotifications.py +77 -52
routes/sendnotifications.py CHANGED
@@ -1,17 +1,25 @@
1
  import os
2
  import aiohttp
3
- from fastapi import APIRouter, HTTPException
4
  from pydantic import BaseModel
5
  from google.oauth2 import service_account
6
  from google.auth.transport.requests import Request
7
 
8
  router = APIRouter()
9
 
10
- # Supabase config (pegando das variáveis de ambiente)
11
- SUPABASE_URL = "https://ussxqnifefkgkaumjann.supabase.co" # Ajuste pro seu projeto
 
12
  SUPABASE_ROLE_KEY = os.getenv("SUPA_SERVICE_KEY")
13
- if not SUPABASE_ROLE_KEY:
14
- raise ValueError("❌ SUPA_SERVICE_KEY not set in environment!")
 
 
 
 
 
 
 
15
 
16
  SUPABASE_ROLE_HEADERS = {
17
  "apikey": SUPABASE_ROLE_KEY,
@@ -24,37 +32,40 @@ SUPABASE_ROLE_HEADERS = {
24
  SERVICE_ACCOUNT_FILE = './closetcoach-2d50b-firebase-adminsdk-fbsvc-7fcccb1.json'
25
  FCM_PROJECT_ID = "closetcoach-2d50b"
26
 
27
- def get_access_token():
28
- credentials = service_account.Credentials.from_service_account_file(
29
- SERVICE_ACCOUNT_FILE
30
- )
31
- scoped_credentials = credentials.with_scopes(
32
- ['https://www.googleapis.com/auth/firebase.messaging']
33
- )
34
- scoped_credentials.refresh(Request())
35
- return scoped_credentials.token
36
-
37
  class NotificationRequest(BaseModel):
38
  keyword: str
39
- initiator_id: str
40
  target_user_id: str
41
 
42
- async def fetch_supabase(table: str, select: str, filters: dict):
43
- # Monta query params
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
44
  filter_query = '&'.join([f'{k}=eq.{v}' for k, v in filters.items()])
45
  url = f"{SUPABASE_URL}/rest/v1/{table}?select={select}&{filter_query}"
46
 
47
  async with aiohttp.ClientSession() as session:
48
- async with session.get(url, headers=SUPABASE_ROLE_HEADERS) as resp:
49
  if resp.status != 200:
50
  detail = await resp.text()
51
  raise HTTPException(status_code=500, detail=f"Erro Supabase: {detail}")
52
  return await resp.json()
53
 
54
- async def check_follow_exists(follower_id: str, following_id: str) -> bool:
55
- result = await fetch_supabase("followers", "id", {"follower_id": follower_id, "following_id": following_id})
56
- return len(result) > 0
57
-
58
  def format_name(full_name: str) -> str:
59
  parts = full_name.strip().split()
60
  if len(parts) == 1:
@@ -67,8 +78,22 @@ async def get_user_info(user_id: str):
67
  return None
68
  return users[0]
69
 
70
- def build_notification_payload(title: str, body: str, token: str) -> dict:
71
- return {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
72
  "message": {
73
  "notification": {
74
  "title": title,
@@ -78,7 +103,6 @@ def build_notification_payload(title: str, body: str, token: str) -> dict:
78
  }
79
  }
80
 
81
- async def send_fcm_notification(payload: dict):
82
  access_token = get_access_token()
83
  headers = {
84
  "Authorization": f"Bearer {access_token}",
@@ -94,35 +118,36 @@ async def send_fcm_notification(payload: dict):
94
  return await resp.json()
95
 
96
  @router.post("/send-notification")
97
- async def send_notification(data: NotificationRequest):
98
- keyword = data.keyword.lower()
99
-
100
- initiator = await get_user_info(data.initiator_id)
101
- if not initiator:
102
- raise HTTPException(status_code=404, detail="Initiator user not found")
103
-
104
- target = await get_user_info(data.target_user_id)
105
- if not target or not target.get("token_fcm"):
106
- raise HTTPException(status_code=404, detail="Target user or token not found")
 
 
 
 
 
 
 
 
 
 
 
 
 
107
 
108
- if keyword == "follow":
109
- if not await check_follow_exists(data.initiator_id, data.target_user_id):
110
- raise HTTPException(status_code=403, detail="Follow relationship not found")
111
-
112
- follower_name = format_name(initiator["name"])
113
  title = "Novo seguidor!"
114
  body = f"{follower_name} começou a seguir você."
115
 
116
- elif keyword == "like":
117
- liker_name = format_name(initiator["name"])
118
- title = "Nova curtida!"
119
- body = f"{liker_name} curtiu sua publicação."
120
-
121
- else:
122
- raise HTTPException(status_code=400, detail="Keyword não suportada")
123
 
124
- payload = build_notification_payload(title, body, target["token_fcm"])
125
 
126
- result = await send_fcm_notification(payload)
127
-
128
- return {"detail": "Notification sent successfully", "fcm_response": result}
 
1
  import os
2
  import aiohttp
3
+ from fastapi import APIRouter, HTTPException, Header
4
  from pydantic import BaseModel
5
  from google.oauth2 import service_account
6
  from google.auth.transport.requests import Request
7
 
8
  router = APIRouter()
9
 
10
+ # 🔧 Supabase Config
11
+ SUPABASE_URL = "https://ussxqnifefkgkaumjann.supabase.co" # ajuste para seu Supabase
12
+ SUPABASE_KEY = os.getenv("SUPA_KEY")
13
  SUPABASE_ROLE_KEY = os.getenv("SUPA_SERVICE_KEY")
14
+
15
+ if not SUPABASE_KEY or not SUPABASE_ROLE_KEY:
16
+ raise ValueError("❌ SUPA_KEY or SUPA_SERVICE_KEY not set in environment!")
17
+
18
+ SUPABASE_HEADERS = {
19
+ "apikey": SUPABASE_KEY,
20
+ "Authorization": f"Bearer {SUPABASE_KEY}",
21
+ "Content-Type": "application/json"
22
+ }
23
 
24
  SUPABASE_ROLE_HEADERS = {
25
  "apikey": SUPABASE_ROLE_KEY,
 
32
  SERVICE_ACCOUNT_FILE = './closetcoach-2d50b-firebase-adminsdk-fbsvc-7fcccb1.json'
33
  FCM_PROJECT_ID = "closetcoach-2d50b"
34
 
 
 
 
 
 
 
 
 
 
 
35
  class NotificationRequest(BaseModel):
36
  keyword: str
 
37
  target_user_id: str
38
 
39
+ async def verify_user_token(user_token: str) -> str:
40
+ headers = {
41
+ "Authorization": f"Bearer {user_token}",
42
+ "apikey": SUPABASE_KEY,
43
+ "Content-Type": "application/json"
44
+ }
45
+
46
+ async with aiohttp.ClientSession() as session:
47
+ async with session.get(f"{SUPABASE_URL}/auth/v1/user", headers=headers) as response:
48
+ if response.status != 200:
49
+ raise HTTPException(status_code=401, detail="Token inválido ou expirado")
50
+
51
+ user_data = await response.json()
52
+ user_id = user_data.get("id")
53
+ if not user_id:
54
+ raise HTTPException(status_code=400, detail="ID do usuário não encontrado")
55
+
56
+ return user_id
57
+
58
+ async def fetch_supabase(table: str, select: str, filters: dict, headers=SUPABASE_ROLE_HEADERS):
59
  filter_query = '&'.join([f'{k}=eq.{v}' for k, v in filters.items()])
60
  url = f"{SUPABASE_URL}/rest/v1/{table}?select={select}&{filter_query}"
61
 
62
  async with aiohttp.ClientSession() as session:
63
+ async with session.get(url, headers=headers) as resp:
64
  if resp.status != 200:
65
  detail = await resp.text()
66
  raise HTTPException(status_code=500, detail=f"Erro Supabase: {detail}")
67
  return await resp.json()
68
 
 
 
 
 
69
  def format_name(full_name: str) -> str:
70
  parts = full_name.strip().split()
71
  if len(parts) == 1:
 
78
  return None
79
  return users[0]
80
 
81
+ async def check_follow_exists(follower_id: str, following_id: str) -> bool:
82
+ result = await fetch_supabase("followers", "id", {"follower_id": follower_id, "following_id": following_id})
83
+ return len(result) > 0
84
+
85
+ def get_access_token():
86
+ credentials = service_account.Credentials.from_service_account_file(
87
+ SERVICE_ACCOUNT_FILE
88
+ )
89
+ scoped_credentials = credentials.with_scopes(
90
+ ['https://www.googleapis.com/auth/firebase.messaging']
91
+ )
92
+ scoped_credentials.refresh(Request())
93
+ return scoped_credentials.token
94
+
95
+ async def send_fcm_notification(title: str, body: str, token: str):
96
+ payload = {
97
  "message": {
98
  "notification": {
99
  "title": title,
 
103
  }
104
  }
105
 
 
106
  access_token = get_access_token()
107
  headers = {
108
  "Authorization": f"Bearer {access_token}",
 
118
  return await resp.json()
119
 
120
  @router.post("/send-notification")
121
+ async def send_notification(
122
+ data: NotificationRequest,
123
+ user_token: str = Header(..., alias="User-key")
124
+ ):
125
+ follower_id = await verify_user_token(user_token)
126
+ target_user_id = data.target_user_id
127
+
128
+ if data.keyword == "follow":
129
+ # verifica se usuário alvo existe e tem token
130
+ target_user = await get_user_info(target_user_id)
131
+ if not target_user or not target_user.get("token_fcm"):
132
+ raise HTTPException(status_code=404, detail="Usuário alvo ou token FCM não encontrado")
133
+
134
+ # verifica se a relação de follow existe mesmo
135
+ if not await check_follow_exists(follower_id, target_user_id):
136
+ raise HTTPException(status_code=403, detail="Relação de follow não encontrada")
137
+
138
+ # pega nome do follower para notificação
139
+ follower = await get_user_info(follower_id)
140
+ if not follower or not follower.get("name"):
141
+ raise HTTPException(status_code=404, detail="Usuário follower não encontrado")
142
+
143
+ follower_name = format_name(follower["name"])
144
 
 
 
 
 
 
145
  title = "Novo seguidor!"
146
  body = f"{follower_name} começou a seguir você."
147
 
148
+ fcm_response = await send_fcm_notification(title, body, target_user["token_fcm"])
 
 
 
 
 
 
149
 
150
+ return {"detail": "Notificação de follow enviada com sucesso", "fcm_response": fcm_response}
151
 
152
+ else:
153
+ raise HTTPException(status_code=400, detail="Keyword não suportada")