File size: 6,008 Bytes
bb722bb aa5273d c98d04b bb722bb aa5273d bb722bb 5ceb41d aa5273d bb722bb aa5273d bb722bb aa5273d c98d04b aa5273d 6114a4a bb722bb 5ceb41d bb722bb 5ceb41d aa5273d 5ceb41d bb722bb aa5273d bb722bb 6114a4a bb722bb aa5273d bb722bb aa5273d bb722bb aa5273d bb722bb aa5273d bb722bb aa5273d bb722bb aa5273d |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 |
import os
import aiohttp
from fastapi import APIRouter, HTTPException, Header
from pydantic import BaseModel
from google.oauth2 import service_account
from google.auth.transport.requests import Request
from datetime import datetime
from typing import Optional
router = APIRouter()
# Supabase Config
SUPABASE_URL = "https://ussxqnifefkgkaumjann.supabase.co"
SUPABASE_KEY = os.getenv("SUPA_KEY")
SUPABASE_SERVICE_KEY = os.getenv("SUPA_SERVICE_KEY")
if not SUPABASE_KEY or not SUPABASE_SERVICE_KEY:
raise ValueError("❌ SUPA_KEY or SUPA_SERVICE_KEY not set in environment!")
SUPABASE_HEADERS = {
"apikey": SUPABASE_SERVICE_KEY,
"Authorization": f"Bearer {SUPABASE_SERVICE_KEY}",
"Content-Type": "application/json"
}
# Firebase config
SERVICE_ACCOUNT_FILE = './closetcoach-2d50b-firebase-adminsdk-fbsvc-7fcccbacb1.json'
FCM_PROJECT_ID = "closetcoach-2d50b"
class SimpleNotification(BaseModel):
target: str # email or "all"
title: str
content: str
image_url: str = ""
def get_fcm_access_token():
credentials = service_account.Credentials.from_service_account_file(
SERVICE_ACCOUNT_FILE
)
scoped = credentials.with_scopes(
['https://www.googleapis.com/auth/firebase.messaging']
)
scoped.refresh(Request())
return scoped.token
async def get_user_id_from_token(user_token: str) -> str:
url = f"{SUPABASE_URL}/auth/v1/user"
headers = {
"Authorization": f"Bearer {user_token}",
"apikey": SUPABASE_KEY,
"Content-Type": "application/json"
}
async with aiohttp.ClientSession() as session:
async with session.get(url, headers=headers) as resp:
if resp.status != 200:
raise HTTPException(status_code=401, detail="Invalid or expired token")
data = await resp.json()
return data.get("id")
async def get_user_by_email(email: str):
url = f"{SUPABASE_URL}/rest/v1/User?email=eq.{email}&select=id,token_fcm"
async with aiohttp.ClientSession() as session:
async with session.get(url, headers=SUPABASE_HEADERS) as resp:
if resp.status != 200:
raise HTTPException(status_code=500, detail="Failed to fetch target user from database")
data = await resp.json()
return data[0] if data else None
async def get_user_by_id(user_id: str):
url = f"{SUPABASE_URL}/rest/v1/User?id=eq.{user_id}&select=id,manage_notifications"
async with aiohttp.ClientSession() as session:
async with session.get(url, headers=SUPABASE_HEADERS) as resp:
if resp.status != 200:
raise HTTPException(status_code=500, detail="Failed to fetch sender user from database")
data = await resp.json()
return data[0] if data else None
async def log_notification(send_by: str, title: str, content: str, target_id: Optional[str], image_url: str):
payload = {
"send_by": send_by,
"title": title,
"content": content,
"target": target_id,
"image_url": image_url or None,
"created_at": datetime.utcnow().isoformat()
}
url = f"{SUPABASE_URL}/rest/v1/fcm_notifications"
async with aiohttp.ClientSession() as session:
async with session.post(url, headers=SUPABASE_HEADERS, json=payload) as resp:
if resp.status not in (200, 201):
detail = await resp.text()
raise HTTPException(status_code=500, detail=f"Failed to log notification: {detail}")
def is_valid_image_url(url: str) -> bool:
valid_extensions = (".jpg", ".jpeg", ".png", ".gif", ".webp")
return url.lower().endswith(valid_extensions)
@router.post("/send-global-notification")
async def send_global_notification(
payload: SimpleNotification,
user_token: str = Header(..., alias="User-key")
):
sender_id = await get_user_id_from_token(user_token)
sender = await get_user_by_id(sender_id)
if not sender or not sender.get("manage_notifications"):
raise HTTPException(status_code=403, detail="You are not authorized to send notifications.")
# Validate image_url, if provided
if payload.image_url and not is_valid_image_url(payload.image_url):
raise HTTPException(status_code=400, detail="The image_url provided is not a valid image format.")
message = {
"notification": {
"title": payload.title,
"body": payload.content
}
}
target_user_id = None
if payload.image_url:
message["notification"]["image"] = payload.image_url
if payload.target.lower() == "all":
message["topic"] = "all"
else:
target_user = await get_user_by_email(payload.target.lower().strip())
if not target_user:
raise HTTPException(status_code=404, detail="The provided email does not correspond to any user.")
if not target_user.get("token_fcm"):
raise HTTPException(status_code=400, detail="The target user does not have a registered FCM token.")
message["token"] = target_user["token_fcm"]
target_user_id = target_user["id"]
# Envia para o Firebase
access_token = get_fcm_access_token()
headers = {
"Authorization": f"Bearer {access_token}",
"Content-Type": "application/json"
}
url = f"https://fcm.googleapis.com/v1/projects/{FCM_PROJECT_ID}/messages:send"
async with aiohttp.ClientSession() as session:
async with session.post(url, headers=headers, json={"message": message}) as resp:
response_text = await resp.text()
if resp.status != 200:
raise HTTPException(status_code=resp.status, detail=f"FCM error: {response_text}")
# Salvar notificação no banco
await log_notification(
send_by=sender_id,
title=payload.title,
content=payload.content,
target_id=target_user_id,
image_url=payload.image_url
)
return {"detail": "Notification sent and logged successfully."} |