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."}