fcm notifs v1

This commit is contained in:
Max Młynarczyk 2025-06-04 17:56:43 +02:00
parent a4427bb5e0
commit 2feca9394f
9 changed files with 157 additions and 6 deletions

2
.gitignore vendored
View file

@ -97,3 +97,5 @@ fly.toml
fly-deploy.yml fly-deploy.yml
Dockerfile Dockerfile
cookies.txt cookies.txt
fcm_secret.json

View file

@ -29,6 +29,7 @@ BASE_DIR = Path(__file__).resolve().parent.parent
# SECURITY WARNING: keep the secret key used in production secret! # SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = os.getenv('SECRET_KEY') SECRET_KEY = os.getenv('SECRET_KEY')
FCM_SERVICE_ACCOUNT = str(BASE_DIR / "file.json")
# SECURITY WARNING: don't run with debug turned on in production! # SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True DEBUG = True

View file

@ -3,6 +3,13 @@ from django.db import models
# Create your models here. # Create your models here.
from django.contrib.auth.models import User from django.contrib.auth.models import User
class UserNotifs(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
device_token = models.CharField(blank=True, null=True)
def __str__(self):
return f"{self.user.username}'s notif token: {self.device_token[:8]}..."
class UserStorage(models.Model): class UserStorage(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE) user = models.OneToOneField(User, on_delete=models.CASCADE)
storage_limit = models.BigIntegerField(default=5 * 1024 * 1024 * 1024) # 5 GB storage_limit = models.BigIntegerField(default=5 * 1024 * 1024 * 1024) # 5 GB

View file

@ -1,9 +1,10 @@
from django.db.models.signals import post_save from django.db.models.signals import post_save
from django.dispatch import receiver from django.dispatch import receiver
from django.contrib.auth.models import User from django.contrib.auth.models import User
from .models import UserStorage from .models import UserStorage, UserNotifs
@receiver(post_save, sender=User) @receiver(post_save, sender=User)
def create_user_storage(sender, instance, created, **kwargs): def create_user_storage(sender, instance, created, **kwargs):
if created: if created:
UserStorage.objects.create(user=instance) UserStorage.objects.create(user=instance)
UserNotifs.objects.create(user=instance)

View file

@ -69,6 +69,8 @@ urlpatterns = [
path('whiteboard/<int:whiteboard_id>/save-images/', views.save_images, name='save_images'), path('whiteboard/<int:whiteboard_id>/save-images/', views.save_images, name='save_images'),
path('whiteboard/<int:whiteboard_id>/upload-image/', views.upload_image, name='upload_image'), path('whiteboard/<int:whiteboard_id>/upload-image/', views.upload_image, name='upload_image'),
path('whiteboard/<int:whiteboard_id>/delete-image/', views.delete_image, name='delete_image'), path('whiteboard/<int:whiteboard_id>/delete-image/', views.delete_image, name='delete_image'),
path('api/update_device_token/', views.update_device_token, name='update_device_token'),

View file

@ -35,7 +35,8 @@ from .forms import RegisterForm
from django.core.files.storage import FileSystemStorage from django.core.files.storage import FileSystemStorage
import uuid import uuid
from django.conf import settings from django.conf import settings
from fcm import send_push_notification
from .models import UserNotifs
def email_verified_required(view_func): def email_verified_required(view_func):
@wraps(view_func) @wraps(view_func)
@ -584,6 +585,12 @@ def send_message(request):
content = request.POST.get('content') content = request.POST.get('content')
receiver = User.objects.get(username=reciever_username) receiver = User.objects.get(username=reciever_username)
message = Message.objects.create(sender=request.user, receiver=receiver, content=content) message = Message.objects.create(sender=request.user, receiver=receiver, content=content)
if hasattr(receiver, 'device_token') and receiver.device_token:
token = receiver.device_token
title = f"New message from {request.user.username}"
body = content[:100]
send_push_notification(token, title, body)
return JsonResponse({"message": "sent", "message_id": message.id}) return JsonResponse({"message": "sent", "message_id": message.id})
else: else:
return JsonResponse({"message": "error"}) return JsonResponse({"message": "error"})
@ -969,3 +976,19 @@ def delete_image(request, whiteboard_id):
return JsonResponse({'error': str(e)}, status=500) return JsonResponse({'error': str(e)}, status=500)
return JsonResponse({'error': 'Invalid request method'}, status=405) return JsonResponse({'error': 'Invalid request method'}, status=405)
def update_device_token(request):
try:
data = json.loads(request.body)
token = data.get('device_token', '').strip()
if not token:
return JsonResponse({"error": "No device_token provided"}, status=400)
user_notifs, created = UserNotifs.objects.get_or_create(user=request.user)
user_notifs.device_token = token
user_notifs.save()
return JsonResponse({"message": "Device token updated"})
except Exception as e:
return JsonResponse({"error": str(e)}, status=500)

33
fcm.py Normal file
View file

@ -0,0 +1,33 @@
import json
import requests
from google.oauth2 import service_account
from django.conf import settings
def send_push_notification(token, title, body, data=None):
creds = service_account.Credentials.from_service_account_file(
settings.FCM_SERVICE_ACCOUNT,
scopes=["https://www.googleapis.com/auth/firebase.messaging"]
)
creds.refresh(requests.Request())
headers = {
"Authorization": f"Bearer {creds.token}",
"Content-Type": "application/json; UTF-8",
}
url = f"https://fcm.googleapis.com/v1/projects/{creds.project_id}/messages:send"
message = {
"message": {
"token": token,
"notification": {
"title": title,
"body": body,
},
"data": data or {}
}
}
response = requests.post(url, headers=headers, data=json.dumps(message))
return response.json()

View file

@ -23,3 +23,4 @@ sentry-sdk[django]
yt-dlp yt-dlp
sendgrid sendgrid
django-recaptcha django-recaptcha
google-auth

View file

@ -38,3 +38,84 @@
const replay = Sentry.getReplay(); const replay = Sentry.getReplay();
replay.start(); replay.start();
</script> </script>
<script src="https://www.gstatic.com/firebasejs/9.23.0/firebase-app-compat.js"></script>
<script src="https://www.gstatic.com/firebasejs/9.23.0/firebase-messaging-compat.js"></script>
<script>
const firebaseConfig = {
apiKey: "AIzaSyCR9FFFiNFt6ORYu7KJHv_fT-aD2fW7bIg",
authDomain: "eversync333.firebaseapp.com",
projectId: "eversync333",
messagingSenderId: "546896423149",
appId: "1:546896423149:web:c06d07957a7080965144e4",
};
// Initialize Firebase app
firebase.initializeApp(firebaseConfig);
// Get Firebase messaging instance
const messaging = firebase.messaging();
// Request permission to send notifications and get FCM token
function requestPermissionAndGetToken() {
Notification.requestPermission().then((permission) => {
if (permission === 'granted') {
messaging.getToken({ vapidKey: 'BKJsJ3Bn5G9aMrF0pzN3cJsgQvclDPhSzpMcl3MS3lzgeXL4M1nuZrT1-7HLsnR4bq86SckhxTiyoISYQUZhuOY' }).then((currentToken) => {
if (currentToken) {
console.log('FCM token:', currentToken);
sendTokenToBackend(currentToken);
} else {
console.log('No registration token available. Request permission to generate one.');
}
}).catch((err) => {
console.error('An error occurred while retrieving token. ', err);
});
} else {
console.log('Notification permission denied.');
}
});
}
// Send the token to your backend to save it
function sendTokenToBackend(token) {
fetch('/api/update_device_token/', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': getCookie('csrftoken'), // Djangos CSRF token
},
body: JSON.stringify({ device_token: token }),
credentials: 'include', // send cookies for auth if needed
})
.then(response => response.json())
.then(data => console.log('Token saved on backend:', data))
.catch(console.error);
}
// Helper to get the CSRF token cookie for Django
function getCookie(name) {
let cookieValue = null;
if (document.cookie && document.cookie !== '') {
const cookies = document.cookie.split(';');
for (let cookie of cookies) {
cookie = cookie.trim();
if (cookie.startsWith(name + '=')) {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
}
}
return cookieValue;
}
// Call this on page load or after user logs in
requestPermissionAndGetToken();
// Optional: listen for foreground messages
messaging.onMessage((payload) => {
console.log('Message received in foreground: ', payload);
// You can show a custom notification or UI update here
});
</script>