Update routes/notifications.py
Browse files- routes/notifications.py +55 -16
routes/notifications.py
CHANGED
@@ -4,6 +4,7 @@ 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 |
|
@@ -25,14 +26,12 @@ SUPABASE_HEADERS = {
|
|
25 |
SERVICE_ACCOUNT_FILE = './closetcoach-2d50b-firebase-adminsdk-fbsvc-7fcccbacb1.json'
|
26 |
FCM_PROJECT_ID = "closetcoach-2d50b"
|
27 |
|
28 |
-
# Modelo da requisição
|
29 |
class SimpleNotification(BaseModel):
|
30 |
-
target: str # "all"
|
31 |
title: str
|
32 |
content: str
|
33 |
image_url: str = ""
|
34 |
|
35 |
-
# Função para obter token do Firebase
|
36 |
def get_fcm_access_token():
|
37 |
credentials = service_account.Credentials.from_service_account_file(
|
38 |
SERVICE_ACCOUNT_FILE
|
@@ -43,7 +42,6 @@ def get_fcm_access_token():
|
|
43 |
scoped.refresh(Request())
|
44 |
return scoped.token
|
45 |
|
46 |
-
# Verifica o token de usuário no Supabase
|
47 |
async def get_user_id_from_token(user_token: str) -> str:
|
48 |
url = f"{SUPABASE_URL}/auth/v1/user"
|
49 |
headers = {
|
@@ -59,27 +57,50 @@ async def get_user_id_from_token(user_token: str) -> str:
|
|
59 |
data = await resp.json()
|
60 |
return data.get("id")
|
61 |
|
62 |
-
|
63 |
-
|
64 |
-
url = f"{SUPABASE_URL}/rest/v1/User?id=eq.{user_id}&select=id,token_fcm,manage_notifications"
|
65 |
async with aiohttp.ClientSession() as session:
|
66 |
async with session.get(url, headers=SUPABASE_HEADERS) as resp:
|
67 |
if resp.status != 200:
|
68 |
-
raise HTTPException(status_code=
|
69 |
data = await resp.json()
|
70 |
return data[0] if data else None
|
71 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
72 |
@router.post("/send-global-notification")
|
73 |
async def send_global_notification(
|
74 |
payload: SimpleNotification,
|
75 |
user_token: str = Header(..., alias="User-key")
|
76 |
):
|
77 |
-
# Verifica o ID do usuário autenticado
|
78 |
sender_id = await get_user_id_from_token(user_token)
|
79 |
-
sender = await
|
80 |
|
81 |
if not sender or not sender.get("manage_notifications"):
|
82 |
-
raise HTTPException(status_code=403, detail="
|
83 |
|
84 |
message = {
|
85 |
"notification": {
|
@@ -88,17 +109,25 @@ async def send_global_notification(
|
|
88 |
}
|
89 |
}
|
90 |
|
|
|
|
|
91 |
if payload.image_url:
|
92 |
message["notification"]["image"] = payload.image_url
|
93 |
|
94 |
-
if payload.target == "all":
|
95 |
message["topic"] = "all"
|
96 |
else:
|
97 |
-
target_user = await
|
98 |
-
|
99 |
-
|
|
|
|
|
|
|
|
|
100 |
message["token"] = target_user["token_fcm"]
|
|
|
101 |
|
|
|
102 |
access_token = get_fcm_access_token()
|
103 |
headers = {
|
104 |
"Authorization": f"Bearer {access_token}",
|
@@ -111,4 +140,14 @@ async def send_global_notification(
|
|
111 |
response_text = await resp.text()
|
112 |
if resp.status != 200:
|
113 |
raise HTTPException(status_code=resp.status, detail=f"FCM error: {response_text}")
|
114 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
4 |
from pydantic import BaseModel
|
5 |
from google.oauth2 import service_account
|
6 |
from google.auth.transport.requests import Request
|
7 |
+
from datetime import datetime
|
8 |
|
9 |
router = APIRouter()
|
10 |
|
|
|
26 |
SERVICE_ACCOUNT_FILE = './closetcoach-2d50b-firebase-adminsdk-fbsvc-7fcccbacb1.json'
|
27 |
FCM_PROJECT_ID = "closetcoach-2d50b"
|
28 |
|
|
|
29 |
class SimpleNotification(BaseModel):
|
30 |
+
target: str # email or "all"
|
31 |
title: str
|
32 |
content: str
|
33 |
image_url: str = ""
|
34 |
|
|
|
35 |
def get_fcm_access_token():
|
36 |
credentials = service_account.Credentials.from_service_account_file(
|
37 |
SERVICE_ACCOUNT_FILE
|
|
|
42 |
scoped.refresh(Request())
|
43 |
return scoped.token
|
44 |
|
|
|
45 |
async def get_user_id_from_token(user_token: str) -> str:
|
46 |
url = f"{SUPABASE_URL}/auth/v1/user"
|
47 |
headers = {
|
|
|
57 |
data = await resp.json()
|
58 |
return data.get("id")
|
59 |
|
60 |
+
async def get_user_by_email(email: str):
|
61 |
+
url = f"{SUPABASE_URL}/rest/v1/User?email=eq.{email}&select=id,token_fcm"
|
|
|
62 |
async with aiohttp.ClientSession() as session:
|
63 |
async with session.get(url, headers=SUPABASE_HEADERS) as resp:
|
64 |
if resp.status != 200:
|
65 |
+
raise HTTPException(status_code=500, detail="Failed to fetch target user from database")
|
66 |
data = await resp.json()
|
67 |
return data[0] if data else None
|
68 |
|
69 |
+
async def get_user_by_id(user_id: str):
|
70 |
+
url = f"{SUPABASE_URL}/rest/v1/User?id=eq.{user_id}&select=id,manage_notifications"
|
71 |
+
async with aiohttp.ClientSession() as session:
|
72 |
+
async with session.get(url, headers=SUPABASE_HEADERS) as resp:
|
73 |
+
if resp.status != 200:
|
74 |
+
raise HTTPException(status_code=500, detail="Failed to fetch sender user from database")
|
75 |
+
data = await resp.json()
|
76 |
+
return data[0] if data else None
|
77 |
+
|
78 |
+
async def log_notification(send_by: str, title: str, content: str, target_id: str | None, image_url: str):
|
79 |
+
payload = {
|
80 |
+
"send_by": send_by,
|
81 |
+
"title": title,
|
82 |
+
"content": content,
|
83 |
+
"target": target_id,
|
84 |
+
"image_url": image_url or None,
|
85 |
+
"created_at": datetime.utcnow().isoformat()
|
86 |
+
}
|
87 |
+
url = f"{SUPABASE_URL}/rest/v1/fcm_notifications"
|
88 |
+
async with aiohttp.ClientSession() as session:
|
89 |
+
async with session.post(url, headers=SUPABASE_HEADERS, json=payload) as resp:
|
90 |
+
if resp.status not in (200, 201):
|
91 |
+
detail = await resp.text()
|
92 |
+
raise HTTPException(status_code=500, detail=f"Failed to log notification: {detail}")
|
93 |
+
|
94 |
@router.post("/send-global-notification")
|
95 |
async def send_global_notification(
|
96 |
payload: SimpleNotification,
|
97 |
user_token: str = Header(..., alias="User-key")
|
98 |
):
|
|
|
99 |
sender_id = await get_user_id_from_token(user_token)
|
100 |
+
sender = await get_user_by_id(sender_id)
|
101 |
|
102 |
if not sender or not sender.get("manage_notifications"):
|
103 |
+
raise HTTPException(status_code=403, detail="You are not authorized to send notifications.")
|
104 |
|
105 |
message = {
|
106 |
"notification": {
|
|
|
109 |
}
|
110 |
}
|
111 |
|
112 |
+
target_user_id = None
|
113 |
+
|
114 |
if payload.image_url:
|
115 |
message["notification"]["image"] = payload.image_url
|
116 |
|
117 |
+
if payload.target.lower() == "all":
|
118 |
message["topic"] = "all"
|
119 |
else:
|
120 |
+
target_user = await get_user_by_email(payload.target.lower().strip())
|
121 |
+
|
122 |
+
if not target_user:
|
123 |
+
raise HTTPException(status_code=404, detail="The provided email does not correspond to any user.")
|
124 |
+
if not target_user.get("token_fcm"):
|
125 |
+
raise HTTPException(status_code=400, detail="The target user does not have a registered FCM token.")
|
126 |
+
|
127 |
message["token"] = target_user["token_fcm"]
|
128 |
+
target_user_id = target_user["id"]
|
129 |
|
130 |
+
# Envia para o Firebase
|
131 |
access_token = get_fcm_access_token()
|
132 |
headers = {
|
133 |
"Authorization": f"Bearer {access_token}",
|
|
|
140 |
response_text = await resp.text()
|
141 |
if resp.status != 200:
|
142 |
raise HTTPException(status_code=resp.status, detail=f"FCM error: {response_text}")
|
143 |
+
|
144 |
+
# Salvar notificação no banco
|
145 |
+
await log_notification(
|
146 |
+
send_by=sender_id,
|
147 |
+
title=payload.title,
|
148 |
+
content=payload.content,
|
149 |
+
target_id=target_user_id,
|
150 |
+
image_url=payload.image_url
|
151 |
+
)
|
152 |
+
|
153 |
+
return {"detail": "Notification sent and logged successfully."}
|