mirror of
https://github.com/rudy3333/eversync.git
synced 2025-07-02 00:56:02 +00:00
fcm notifs v1
This commit is contained in:
parent
a4427bb5e0
commit
2feca9394f
9 changed files with 157 additions and 6 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -97,3 +97,5 @@ fly.toml
|
||||||
fly-deploy.yml
|
fly-deploy.yml
|
||||||
Dockerfile
|
Dockerfile
|
||||||
cookies.txt
|
cookies.txt
|
||||||
|
|
||||||
|
fcm_secret.json
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
|
@ -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'),
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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
33
fcm.py
Normal 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()
|
|
@ -23,3 +23,4 @@ sentry-sdk[django]
|
||||||
yt-dlp
|
yt-dlp
|
||||||
sendgrid
|
sendgrid
|
||||||
django-recaptcha
|
django-recaptcha
|
||||||
|
google-auth
|
|
@ -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'), // Django’s 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>
|
Loading…
Add table
Add a link
Reference in a new issue