I am setting up push notifications using service workers + websockets using Django Channels and Django WebPush to receive the subscription and send a message back to the service worker.
While I am able to register a service worker and receive the PushSubscription object containing details of a push subscription. I then try to send this subscription to an end point in webpush 'save_information' that should have the subscription. However, when I attempt to do this I get the error:
Response {type: 'cors', url: 'http://127.0.0.1:8000/webpush/save_information', redirected: false, status: 400, ok: false, …}body: (...)bodyUsed: falseheaders: Headers {}ok: falseredirected: falsestatus: 400statusText: "Bad Request"type: "cors"url: "http://127.0.0.1:8000/webpush/save_information"[[Prototype]]: Response
This is the code I have in my service worker/ the code related to getting a subscription and sending through the subscription:
const saveSubscription = async (subscription) => {
const browser = navigator.userAgent.match(/(firefox|msie|chrome|safari|trident)/ig)[0].toLowerCase();
const data = {
status_type: 'subscribe',
subscription: subscription.toJSON(),
browser: browser,
};
const res = await fetch('http://127.0.0.1:8000/webpush/save_information', {
method: 'POST',
body: JSON.stringify(data),
headers: {
'content-type': 'application/json'
},
credentials: 'include'
});
handleResponse(res);
}
const handleResponse = (res) => {
console.log(res);
}
...
self.addEventListener('activate', async () => {
try {
const applicationServerKey = urlB64ToUint8Array('***')
const options = { applicationServerKey,
userVisibleOnly: true }
const subscription = await self.registration.pushManager.subscribe(options)
saveSubscription(subscription)
} catch (err) {
console.log('Error', err)
}
})
and in my server:
path('webpush/', include('webpush.urls')),
Would appreciate any help
Related
Currently the presigned url is generated from Python Lambda function and testing it on postman to upload the file works perfectly.
When uploading file from React.js using axios it fails with 403 status code and below error.
Code: SignatureDoesNotMatch
Message: The request signature we calculated does not match the signature you provided. Check your key and signing method
Python Code
import boto3
s3_client = boto3.client('s3')
params = {
'Bucket': 'bucket_name',
'Key': 'unique_id.pdf',
'ContentType': "application/pdf"
}
s3_response = s3_client.generate_presigned_url(ClientMethod='put_object', Params=params, ExpiresIn=300)
React Code
const readFileDataAsBuffer = (file) =>
new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = (event) => {
resolve(event.target.result);
};
reader.onerror = (err) => {
reject(err);
};
reader.readAsArrayBuffer(file);
});
const onFileUploadChange = async (e) => {
const file = e.target.files[0];
const tempdata = await readFileDataAsBuffer(file);
return axios({
method: 'put',
url: presigned_url_link,
data: tempdata
})
.then(() => {})
.catch(() => {});
};
passing Content-Type worked for s3 link while uploading
I am developing one application that makes use of the wavesurfer.js package. The code is all built in HTML, and it is being rendered using Jinja2 (inside FastAPI). As the audio file is uploaded over the Google Cloud Storage, it was chosen to make a signed url, that allows the file to be accessed by about 180 seconds.
The curious (and awkward) part of the problem, is that the audio file is being accessed, since I can hear the audio using the wavesurfer interface. However, the waveform is not being drawed and the requisition done to the google cloud file is returning a CORS 'Missing Allow Origin' error (Access-Control-Allow-Origin Header).
I have tried to insert it in many ways, following another issues reported at the community, such as intercepting requisitions using the wavesurfer ajax object, as it follows:
WaveSurfer.util.ajax = function (options) {
var ajax = Object.create(WaveSurfer.Observer);
var xhr = new XMLHttpRequest();
var fired100 = false;
xhr.open(options.method || 'GET', options.url, true);
// START added code
xhr.setRequestHeader("Access-Control-Allow-Origin", "*")
xhr.setRequestHeader("Authorization", "Bearer {{token}}")
// END added code
xhr.responseType = options.responseType || 'json';
xhr.addEventListener('progress', function (e) {
ajax.fireEvent('progress', e);
if (e.lengthComputable && e.loaded == e.total) {
fired100 = true;
}
});
xhr.addEventListener('load', function (e) {
if (!fired100) {
ajax.fireEvent('progress', e);
}
ajax.fireEvent('load', e);
if (200 == xhr.status || 206 == xhr.status) {
ajax.fireEvent('success', xhr.response, e);
} else {
ajax.fireEvent('error', e);
}
});
xhr.addEventListener('error', function (e) {
ajax.fireEvent('error', e);
});
xhr.send();
ajax.xhr = xhr;
return ajax;
}
Forcing the token and the 'origins' and 'Access-Control-Allow-Origin' headers when I generate the signed url, as it follows:
file = storage.Blob(file_path, self.bucket)
credentials = service_account.Credentials.from_service_account_file('app/credentials.json', scopes=['https://www.googleapis.com/auth/cloud-platform'])
auth_req = rq.Request()
credentials.refresh(auth_req)
token = credentials.token
headers = {
'Origin': '*',
'Access-Control-Allow-Origin': '*'
}
url = file.generate_signed_url(expiration=180, version='v4', access_token=token, headers=headers)
Or even placing the 'xhr' object inside the WaveSurfer.create({...}), as it follows:
let wavesurfer = WaveSurfer.create({
container: '#waveform',
waveColor: 'violet',
progressColor: 'purple',
backend: 'MediaElement',
plugins: [
WaveSurfer.elan.create({
url: "{{ elan_file_url }}",
container: '#annotations',
tiers: {
Text: true,
Comments: true
}
}),
WaveSurfer.regions.create()
],
xhr: {
cache: 'default',
mode: 'cors',
method: 'GET',
credentials: 'same-origin',
redirect: 'follow',
referrer: 'client',
headers: [
{ key: 'Authorization', value: 'Bearer {{ token }}' },
{ key: 'Access-Control-Allow-Origin', value: 'origin-list' }
]}
As a measure to try to overcome this, I also have attempted to generate the waveform using a repo called 'py-wav2json', that worked (in parts), but the waveform was very away from the expected and I was hoping not to need to generate every single waveform, because it will let the system very overloaded.
Is there any posible measure to overcome this situation?
I am sending the message from a Python script as follows.
import firebase_admin
from firebase_admin import credentials, messaging
# initializations
cred = credentials.Certificate('Full path to firebase-admin-sdk.json')
print('Connecting...')
firebase_admin.initialize_app(cred)
registration_tokens = [
'valid_registration_token_from_client',
]
message = messaging.MulticastMessage(
data={'score': '850', 'time': '2:45'},
tokens = registration_tokens
)
response = messaging.send_multicast(message)
print('{0} messages were sent successfully.'.format(response.success_count))
After executing the above code, it prints 1 message sent successfully. I looked at my Firebase console and found that the number of sent notifications increased by 1. However, my React Js client does not seem to receive the message.
In react app,
Root public folder has firebase-messaging-sw.js file,
import { initializeApp } from "firebase/app";
import { getMessaging } from "firebase/messaging/sw";
import { onBackgroundMessage } from "firebase/messaging/sw";
const firebaseConfig = {
apiKey: "MY_API_KEY",
authDomain: "FIREBASE_APP_DOMAIN",
databaseURL: "DB_URL",
projectId: "PROJECT_ID",
storageBucket: "STORAGE_BUCKET",
messagingSenderId: "SENDER_ID",
appId: "APP_ID",
measurementId: "MEASUREMENT_ID"
};
const firebaseApp = initializeApp(firebaseConfig);
const messaging = getMessaging(firebaseApp);
onBackgroundMessage(messaging, (payload) => {
console.log('[firebase-messaging-sw.js] Received background message ', payload);
// Customize notification here
const notificationTitle = 'Background Message Title';
const notificationOptions = {
body: 'Background Message body.',
icon: '/firebase-logo.png'
};
self.registration.showNotification(notificationTitle,
notificationOptions);
});
In App.js,
import React, {Component} from "react";
import { getMessaging, getToken, onMessage } from "firebase/messaging";
import { initializeApp } from "firebase/app";
export default class App extends Component {
constructor(props) {
super(props);
this.connectButtonPressed = this.connectButtonPressed.bind(this);
}
render() {
return (
<div><button onClick={this.connectButtonPressed}>Click</button></div>
)
}
connectButtonPressed(e) {
Notification.requestPermission().then((permission) => {
if (permission === 'granted') {
console.log('Notification permission granted.');
// TODO(developer): Retrieve a registration token for use with FCM.
// Get registration token. Initially this makes a network call, once retrieved
// subsequent calls to getToken will return from cache.
const firebaseConfig = {
apiKey: "API_KEY",
authDomain: "AUTH_DOMAIN",
databaseURL: "DB_URL",
projectId: "PROJECT_ID",
storageBucket: "STORAGE_BUCKET",
messagingSenderId: "SENDER_ID",
appId: "APP_ID",
measurementId: "MEASUREMENT_ID"
};
const firebaseApp = initializeApp(firebaseConfig);
const messaging = getMessaging(firebaseApp);
onMessage(messaging, (payload) => {
console.log('Message received. ', payload);
});
getToken(messaging, { vapidKey: 'VAPID_KEY_FROM_CONSOLE' }).then((currentToken) => {
if (currentToken) {
// Send the token to your server and update the UI if necessary
console.log('currentToken: ', currentToken);
} else {
// Show permission request UI
console.log('No registration token available. Request permission to generate one.');
// ...
}
}).catch((err) => {
console.log('An error occurred while retrieving token. ', err);
// ...
});
} else {
console.log('Unable to get permission to notify.');
}
});
}
}
The client can successfully request for and receive the registration token to which I send the message from the Python script. However, the onMessage event is not being triggered which makes me think that the client might not be receiving the message even though I sent the message to the token associated with this client.
What might be the issue here? What am I missing?
how can i send a RPC message (based on rabbitmq)from a reactJs project in frontend to a python project in backend .
i tried to use amqplib to send my message but i think i can't use it in a browser .
any help !
this is my code :
export function callService() {
var amqp = require('amqplib/callback_api');
amqp.connect('amqp://localhost:8000', function (err, conn) {
console.log("gggggggg")
conn.createChannel(function(err, ch) {
var q = 'frontend';
var msg = 'Hello!';
let messsage_body="http://www.documents.sergic.com/photos/photo-location-appartement-1-piece-pontoise-95300_563687_1_101_S.JPG";
var headers={'type': 'get_similarity', 'customer': 'sergic'};
console.log('cc ', num);
ch.consume(q.queue, function(msg) {
if (msg.properties.correlationId === corr) {
console.log(' [.] Got %s', msg.content.toString());
setTimeout(function() { conn.close(); process.exit(0) }, 500);
}
}, {noAck: true});
});
}
and i want to run it in a browser by clicking on a picture !
I need to create an application, where GAE server will always talk with just one client (i.e. one message should be always sent just to one client).
I do the following -
Python:
def get(self):
# generate token, when page is loaded
client_id = uuid.uuid4().hex
token = channel.create_channel(client_id)
template_values = {'token': token,
'client_id': client_id
}
self.response.out.write(template.render('page.html', template_values))
def post(self):
# reply to the client
...
client_id = self.request.get('id')
channel.send_message(client_id, message)
Javascript:
sendMessage = function(field) {
$.ajax({
type: "POST",
url: "/",
data: "f=" + field + "&id=" + "{{ client_id }}", // WARNING!
contentType: "application/x-www-form-urlencoded",
success: function(data) {
}
});
};
onOpened = function() {
connected = true;
sendMessage('opened');
};
onMessage = function(msg) {
alert(msg.data);
};
onError = function(err) {
alert(err);
};
onClose = function() {
alert("close");
};
// open new session
channel = new goog.appengine.Channel('{{ token }}'); // WARNING!
socket = channel.open();
socket.onopen = onOpened;
socket.onmessage = onMessage;
socket.onerror = onError;
socket.onclose = onClose;
It works well, but with such scenario both token and client_id are passed to the client. Is it OK?
There's no technical reason not to do this. If you're worried about security, the token is far more valuable: an attacker who could listen to your traffic could take the token and listen to channel messages in a different context. The clientid wouldn't let them do that.
But I do have a question: why not return the message in the POST response, rather than sending a message over the channel? Or is the sample code just simplified for the example?