Hydra-Bolt commited on
Commit
dcabd54
·
1 Parent(s): a83321d
Files changed (6) hide show
  1. .gitignore +2 -0
  2. Dockerfile +28 -0
  3. __pycache__/main.cpython-313.pyc +0 -0
  4. main.py +94 -0
  5. requirements.txt +46 -0
  6. server.js +43 -0
.gitignore ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ serviceAccountKey.json
2
+ .env
Dockerfile ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Dockerfile for Hugging Face Spaces (FastAPI + Firebase)
2
+ FROM python:3.10-slim
3
+
4
+ # Set working directory
5
+ WORKDIR /app
6
+
7
+ # Install system dependencies
8
+ RUN apt-get update && apt-get install -y \
9
+ build-essential \
10
+ && rm -rf /var/lib/apt/lists/*
11
+
12
+ # Copy requirements
13
+ COPY requirements.txt ./
14
+
15
+ # Install Python dependencies
16
+ RUN pip install --no-cache-dir -r requirements.txt
17
+
18
+ # Copy application code
19
+ COPY . .
20
+
21
+ # Expose port (Hugging Face Spaces expects 7860, but FastAPI default is 3000)
22
+ EXPOSE 7860
23
+
24
+ # Set environment variable for Hugging Face Spaces
25
+ ENV PORT=7860
26
+
27
+ # Start FastAPI server on the expected port
28
+ CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "7860"]
__pycache__/main.cpython-313.pyc ADDED
Binary file (4.47 kB). View file
 
main.py ADDED
@@ -0,0 +1,94 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import FastAPI, HTTPException
2
+ from pydantic import BaseModel
3
+ from typing import List, Optional, Dict, Any
4
+ import firebase_admin
5
+ from firebase_admin import credentials, messaging
6
+ import json
7
+ import os
8
+ from dotenv import load_dotenv
9
+
10
+ # Load environment variables from .env file
11
+ load_dotenv()
12
+ # Load Firebase service account key
13
+ service_account_json = os.environ.get("FIREBASE_SERVICE_ACCOUNT_JSON")
14
+ if not service_account_json:
15
+ raise EnvironmentError("FIREBASE_SERVICE_ACCOUNT_JSON environment variable not set")
16
+
17
+ service_account = json.loads(service_account_json)
18
+
19
+ # Initialize Firebase Admin SDK
20
+ cred = credentials.Certificate(service_account)
21
+ firebase_admin.initialize_app(cred)
22
+
23
+ # Create FastAPI app
24
+ app = FastAPI(
25
+ title="FCM Notification Server",
26
+ description="A FastAPI server for sending Firebase Cloud Messaging notifications",
27
+ version="1.0.0"
28
+ )
29
+
30
+ # Pydantic models for request/response
31
+ class NotificationRequest(BaseModel):
32
+ tokens: List[str]
33
+ title: str
34
+ body: str
35
+ data: Optional[Dict[str, Any]] = {}
36
+
37
+ class NotificationResponse(BaseModel):
38
+ success: bool
39
+ response: Optional[Dict[str, Any]] = None
40
+ error: Optional[str] = None
41
+
42
+ @app.get("/")
43
+ async def root():
44
+ """Root endpoint to check if the server is running"""
45
+ return {"message": "🚀 FCM FastAPI Server is running!"}
46
+
47
+ @app.post("/send", response_model=NotificationResponse)
48
+ async def send_notification(request: NotificationRequest):
49
+ """Send notification to multiple tokens"""
50
+ try:
51
+ if not request.tokens or len(request.tokens) == 0:
52
+ raise HTTPException(status_code=400, detail="No tokens provided")
53
+
54
+ # Create the message
55
+ message = messaging.MulticastMessage(
56
+ notification=messaging.Notification(
57
+ title=request.title,
58
+ body=request.body
59
+ ),
60
+ data=request.data or {},
61
+ tokens=request.tokens
62
+ )
63
+
64
+ # Send the message
65
+ response = messaging.send_each_for_multicast(message)
66
+
67
+ return NotificationResponse(
68
+ success=True,
69
+ response={
70
+ "success_count": response.success_count,
71
+ "failure_count": response.failure_count,
72
+ "responses": [
73
+ {
74
+ "success": resp.success,
75
+ "message_id": resp.message_id if resp.success else None,
76
+ "exception": str(resp.exception) if resp.exception else None
77
+ }
78
+ for resp in response.responses
79
+ ]
80
+ }
81
+ )
82
+
83
+ except Exception as error:
84
+ print(f"Error sending notification: {error}")
85
+ raise HTTPException(status_code=500, detail=str(error))
86
+
87
+ @app.get("/health")
88
+ async def health_check():
89
+ """Health check endpoint"""
90
+ return {"status": "healthy", "service": "FCM Notification Server"}
91
+
92
+ if __name__ == "__main__":
93
+ import uvicorn
94
+ uvicorn.run(app, host="0.0.0.0", port=3000)
requirements.txt ADDED
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ annotated-types==0.7.0
2
+ anyio==4.10.0
3
+ CacheControl==0.14.3
4
+ cachetools==5.5.2
5
+ certifi==2025.8.3
6
+ cffi==2.0.0
7
+ charset-normalizer==3.4.3
8
+ click==8.3.0
9
+ cryptography==46.0.1
10
+ fastapi==0.117.1
11
+ firebase_admin==7.1.0
12
+ google-api-core==2.25.1
13
+ google-auth==2.40.3
14
+ google-cloud-core==2.4.3
15
+ google-cloud-firestore==2.21.0
16
+ google-cloud-storage==3.4.0
17
+ google-crc32c==1.7.1
18
+ google-resumable-media==2.7.2
19
+ googleapis-common-protos==1.70.0
20
+ grpcio==1.75.0
21
+ grpcio-status==1.75.0
22
+ h11==0.16.0
23
+ h2==4.3.0
24
+ hpack==4.1.0
25
+ httpcore==1.0.9
26
+ httpx==0.28.1
27
+ hyperframe==6.1.0
28
+ idna==3.10
29
+ msgpack==1.1.1
30
+ proto-plus==1.26.1
31
+ protobuf==6.32.1
32
+ pyasn1==0.6.1
33
+ pyasn1_modules==0.4.2
34
+ pycparser==2.23
35
+ pydantic==2.11.9
36
+ pydantic_core==2.33.2
37
+ PyJWT==2.10.1
38
+ python-dotenv==1.1.1
39
+ requests==2.32.5
40
+ rsa==4.9.1
41
+ sniffio==1.3.1
42
+ starlette==0.48.0
43
+ typing-inspection==0.4.1
44
+ typing_extensions==4.15.0
45
+ urllib3==2.5.0
46
+ uvicorn==0.36.0
server.js ADDED
@@ -0,0 +1,43 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import express from "express";
2
+ import bodyParser from "body-parser";
3
+ import admin from "firebase-admin";
4
+ import fs from "fs";
5
+
6
+ // Load Firebase service account key
7
+ const serviceAccount = JSON.parse(
8
+ fs.readFileSync("./serviceAccountKey.json", "utf-8")
9
+ );
10
+
11
+ admin.initializeApp({
12
+ credential: admin.credential.cert(serviceAccount),
13
+ });
14
+
15
+ const app = express();
16
+ app.use(bodyParser.json());
17
+
18
+ // Send notification to multiple tokens
19
+ app.post("/send", async (req, res) => {
20
+ try {
21
+ const { tokens, title, body, data } = req.body;
22
+
23
+ if (!tokens || tokens.length === 0) {
24
+ return res.status(400).json({ error: "No tokens provided" });
25
+ }
26
+
27
+ const message = {
28
+ notification: { title, body },
29
+ data: data || {}, // custom key-value data
30
+ tokens,
31
+ };
32
+
33
+ const response = await admin.messaging().sendEachForMulticast(message);
34
+ res.json({ success: true, response });
35
+ } catch (error) {
36
+ console.error("Error sending notification:", error);
37
+ res.status(500).json({ error: error.message });
38
+ }
39
+ });
40
+
41
+ app.listen(3000, () => {
42
+ console.log("🚀 FCM Server running on http://localhost:3000");
43
+ });