How to send user specific data using SSE - javascript

i am trying to create a social media page where in home page of every user they can see feeds,
feeds from friend's post. when ever my friend create a post i can see the same in my feeds in real time.
For that i am using SSE in python flask. everything working find but after adding few more users only i realise all the post are coming to all logged in people's feed. which wrong, i want to see feeds from only my friends.
Can any one help me how to achieve it. i am sharing the base level code of python and java script.
Client side:
var source = new EventSource("http://172.19.0.3:8044/events");
source.addEventListener('user_feeds', function(event) {
var data = JSON.parse(event.data);
console.log("Even from server ");
console.log(data);
}, false);
Server side
from flask_cors import CORS
from flask_sse import sse
from factory import create_app
app = create_app()
CORS(app)
app.config["REDIS_URL"] = "redis://redis"
input_user_feeds = dict()
app.register_blueprint(sse, url_prefix='/events)
PROMOTION_BLUEPRINT = Blueprint('my_page', __name__, url_prefix='/api/v1/')
#PROMOTION_BLUEPRINT.route('/feeds/<user_id>', methods=["GET"])
def feeds(user_id):
push_feeds(user_id)
return "SUCCESS"
#PROMOTION_BLUEPRINT.route('/user_request/<user_id>', methods=["POST"])
def user_request(user_id):
data = request.json
add_feeds(user_id, data)
return "SUCESS"
def push_feeds(user_id):
while 1 == 1:
if user_id in input_user_feeds:
input_request = input_user_feeds[user_id]
sse.publish(input_request, type='user_feeds')
del input_user_feeds[user_id]
def add_feeds(user_id, data):
input_user_feeds[user_id] = data
if __name__ == '__main__':
app.run(host='0.0.0.0', port=Config.PORT, debug=True)
I trued to do with single user id. but that is also not a good idea.
It will be helpful if anyone having good knowledge in SSE help me find the solution.
Thanks in advance.

Related

Data posted to flask endpoint from JS not processed in endpoint

I have written a simple todo app with react acting as a frontend and flask handling CRUD from a DB. The app is using axios to handle the requests; GET completes fine however when attempting to POST JSON the flask api returns a 400 error. Here's some condensed sample code.
JS POST function.
function testPost(){
axios.post('http://'+window.location.hostname+':8000/todo/', {
title: "test123",
}).then(res => {
console.log(res)
}).catch(err => {
console.log(err)
})
}
Serverside
class Todo(Resource):
def post(self): # create a new todo
conn = pool.getconn()
cur = conn.cursor()
app.logger.info(request.form['title'])
cur.execute("INSERT INTO todo (task, done) VALUES (%s, %s)", (request.form['title'], False))
conn.commit()
app.logger.error(e)
cur.close()
pool.putconn(conn)
Other methods not shown
Then the rest of the server code attaching the resource to the api and the CORS setup (not shown in file order)
app = Flask(__name__)
CORS(app, methods=['POST','GET','PUT','DELETE'])
api = Api(app)
api.add_resource(Todo, '/todo/')
app.run(debug = True, host='0.0.0.0', port=port)
Tests
Using python to test the api works fine, running this in a seperate python file will add to the DB.
response = requests.post(URL + "todo/", data={"title": f"test{randint(1, 100)}"})
My best guess is that axios is not adding the data to the request in a way that the backend is unable to process. Before using axios I tried to make the request with XMLHttprequest however this presented the same problem. I swapped to axios on the recommendation of someone else, given its alleged improved simplicity.
request.form['key'] and request.get_json()['key'] are completely different fields python requests in the way I used it posts to the former and js posts to the latter. Modifying the function to use whichever is available fixes this.

How to detect when a user Close browser/tab in flask back-end not in front-end (if not valid javascript)

As I'm using the session to close after 10min the user is inactive. But now I want to clear the Session when a user closes the browser or tab.
from flask import Flask, render_template, request, session, redirect, url_for, flash, Response, Blueprint, jsonify, \
send_file,copy_current_request_context
import string
import random
import base64
import binascii
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad, unpad
import os
app = Flask(__name__,template_folder='templates',static_folder='static')
secret_key_=os.urandom(24)
app.secret_key = secret_key_
app.config['SESSION_TYPE'] = 'filesystem'
app.debug = True
BLOCK_SIZE = 16
def pad(data):
length = BLOCK_SIZE - (len(data) % BLOCK_SIZE)
return data + chr(length)*length
def unpad(data):
data = data[:-data[-1]]
return data
def decrypt(encrypted, key):
BLOCK_SIZE = 16
encrypted = base64.b64decode(encrypted)
IV = encrypted[:BLOCK_SIZE]
aes = AES.new(key[0:16], AES.MODE_CBC, IV)
return unpad(aes.decrypt(encrypted[BLOCK_SIZE:]))
#app.route('/', methods=["GET", "POST"])
def login():
if request.method == "OPTIONS":
return 403
else:
return render_template("home.html")
#app.route('/encryp', methods=["GET", "POST"])
def encryp():
if request.method == "OPTIONS":
return 403
else:
if session.get('page_buffer') is None:
print('here123')
key = b"gAAAAABfafxf7_uZ--GzUq5GMBc6h"
temp_=decrypt(str(request.form["fname"]),key)
temp_1 = decrypt(str(request.form["lname12"]), key)
session['page_buffer'] = "Yes"
session['fname']=temp_
session['lname12'] = temp_1
fname=session.get('fname')
password = session.get('lname12')
#password = decrypt(str(request.form["lname12"]),key)
return render_template("Cep.html",Name12=fname,Password12=password)
else:
print('here')
fname = session.get('fname')
password = session.get('lname12')
# password = decrypt(str(request.form["lname12"]),key)
return render_template("Cep.html", Name12=fname, Password12=password)
if __name__ == '__main__':
app.run()
Can Anyone please help me how to detect a user closing a browser or a tab in the flask? Because user should not be viewing the same page once he closes the browser/tab.
JavaScript examples (not working):
The below two are not working because when I'm redirecting to another page these two are getting hit but I don't want that I only want to monitor browser close.
window.addEventListener('beforeunload', function(event) {
console.log('I am the 2nd one.');
});
window.addEventListener('unload', function(event) {
console.log('I am the 4th and last oneā€¦');
});
if you can suggest a better way to use JavaScript then it is okay for me.
You could try to set a cookie and once the route is accessed again, make Flask check whether such a cookie already exists. If it does, you know the user has been there before.
Cookie behavior is largely altered by browser though and can also be manipulated by users easily.
A more reliable and safer way would be to save it in a database, in case you can identify the user by email or something alike.

Trigger javascript function from Django server

I am working on IPN's. Whenever I receive an IPN in this url: https://www.mywebsitename.com/notifications, I would like to run a simple JavaScript function that displays some html content.
I manage the IPN's from the server side, as follows:
#csrf_exempt
def notifications(request):
if request.method == "POST":
#some code
I would like to trigger my JS function inside that block of code, but I can't come up with any way of doing this. Also I don't really know wether what I am asking is really possible or not, maybe there is another approach that I can't figure out by myself.
This is 100% possible with Django using Websockets. It sounds like you are trying to build a notification system for the UI based on when you receive a request at one of your urls. Start by checking out Django Channels: https://channels.readthedocs.io/en/stable/
This is the best way to use Websockets with Django, which can help you implement notifications or other real-time features. The tutorial in the docs is great. To solve your task, you should:
Follow the tutorial and set up a WebSocket consumer in your Django app to handle notifications. This is what will allow a frontend user to establish a real-time connection with your application and receive messages from it.
Finish your notifications_view. After the request from the payment comes in, you will be dispatching a message to your websocket consumer for whichever user needs to recieve the message. It could end up looking something like this:
# assuming that the request gives you the username of the user to be notified,
# and you have a group configured in your consumer
def notification_view(request):
if request.method == "POST":
username = request.POST.get('username')
group_name = 'notifications{}'.format(username)
channel_layer = channels.layers.get_channel_layer()
async_to_sync(channel_layer.group_send)(
group_name,
{
'type': 'new_payment',
'text': {
'eventType': 'notification',
'name': 'New Payment',
'userMessage': 'You just got paid'
}
}
return(HttpResponse(status=200))
This would send a message over the user's socket when the request is received.
In your js, you will setup a listener for the socket. Then you can listen for the messages and do whatever you desire in the document with the data you recieve, such as show a user a message:
var endpoint = 'ws://' + '<yourhost>/notifications/username/;
var socket = new ReconnectingWebSocket(endpoint);
socket.onmessage = e => {
data = JSON.parse(e.data);
let msg = document.createElement('<p>');
msg.innerText = data['text']['userMessage']
}
Just follow the tutorial and that will certainly get you headed in the right direction!

Sending data from JavaScript to Python function locally with AJAX

I am trying to build a website where a user can enter text, which will be picked up via javascript, and sent to a python function where it will be posted to twitter. For the time being, the python function is being stored locally, along with the rest of the site. However, my AJAX isn't too great and I'm having a few issues.
I have written AJAX code which sends a POST request to the python function with the tweet, and the response is the entire python script. No connection is made to the socket my script is listening to. Below is the AJAX function and the python script. Any ideas what's going on?
Thanks in advance for any help!
$(function(){
$('#PostTweet').on('click', function(e) {
var tweet = document.getElementById("theTweet").value;
var len = tweet.length;
if(len > 140){
window.alert("Tweet too long. Please remove some characters");
}else{
callPython(tweet);
}
});
});
function callPython(tweet){
window.alert("sending");
$.ajax({
type: "POST",
url: "tweet.py",
data: tweet,
success: function(response){
window.alert(response);
}
})
}
And the Python Script:
from OAuthSettings import settings
import twitter
from socket import *
consumer_key = settings['consumer_key']
consumer_secret = settings['consumer_secret']
access_token_key = settings['access_token_key']
access_token_secret = settings['access_token_secret']
s = socket()
s.bind(('', 9999))
s.listen(4)
(ns, na) = s.accept()
def PostToTwits(data):
try:
api = twitter.Api(
consumer_key = consumer_key,
consumer_secret = consumer_secret,
access_token_key = access_token_key,
access_token_secret = access_token_secret)
api.PostUpdate(data)
makeConnection(s)
except twitter.TwitterError:
print 'Post Unsuccessful. Error Occurred'
def makeConnection(s):
while True:
print "connected with: " + str(na)
try:
data = ns.recv(4096)
print data
PostToTwits(data)
except:
ns.close()
s.close()
break
makeConnection(s)
Your problem is that you are working with pure sockets which know nothing about HTTP protocol. Take a look at Flask or Bottle web micro frameworks to see how to turn python script or function into web endpoint.
you need a webserver so that your can make request via web browser.
you can web framework like flask or django or you can use webpy.
A simple example using webpy from their website
import web
urls = (
'/(.*)', 'hello'
)
app = web.application(urls, globals())
class hello:
def GET(self, name):
if not name:
name = 'World'
return 'Hello, ' + name + '!'
if __name__ == "__main__":
app.run()
then you call url(your python function) from javascript.
You can totally write a simple web server using sockets, and indeed you've done so. But this approach will quickly get tedious for anything beyond a simple exercise.
For example, your code is restricted to handling a single request handler, which goes to the heart of your problem.
The url on the post request is wrong. In your setup there is no notion of a url "tweet.py". That url would actually work if you were also serving the web page where the jquery lives from the same server (but you can't be).
You have to post to "http://localhost:9999" and you can have any path you want after:"http://localhost:9999/foo", "http://localhost:9999/boo". Just make sure you run the python script from the command line first, so the server is listening.
Also the difference between a get and a post request is part of the HTTP protocol which your simple server doesn't know anything about. This mainly means that it doesn't matter what verb you use on the ajax request. Your server listens for all HTTP verb types.
Lastly, I'm not seeing any data being returned to the client. You need to do something like ns.sendall("Some response"). Tutorials for building a simple http server abound and show different ways of sending responses.

How to combine Tornado authentication and AngularJS?

I am using the authentication system provided as an example in the tornado documentation. When I want to combine it with AngularJS - AngularJS complains about a Cross-Origin Request.
How can I combine Tornado's authentication system and AngularJS?
Authentication Handlers
class BaseHandler(tornado.web.RequestHandler):
def get_current_user(self):
user_json = self.get_secure_cookie("my_app")
if not user_json: return None
return tornado.escape.json_decode(user_json)
class AuthGoogleLoginHandler(BaseHandler, tornado.auth.GoogleMixin):
'''
The Google OAuth Handler.
The `AuthGoogleLoginHandler` redirects each accepted user
to the index /.
'''
#gen.coroutine
def get(self):
if self.get_argument("openid.mode", None):
user = yield self.get_authenticated_user()
self.set_secure_cookie("my_app",
tornado.escape.json_encode(user))
self.current_user = user
email = user.get('email')
try:
usr = models.User.objects.get(email=email)
except mongoengine.DoesNotExist as e:
# there is no user with the wished email address
# let's create a new one.
new_user = models.User()
new_user.email = user.get('email')
new_user.first_name = user.get('first_name')
new_user.last_name = user.get('last_name')
new_user.locale = user.get('locale')
new_user.save()
self.redirect(self.get_argument('next', '/'))
return
self.authenticate_redirect()
ProfileHandler
class ProfileHandler(BaseHandler):
'''
returns the username of the current user so that it can be used by the AngularJS Templates
'''
#tornado.web.authenticated
def get(self):
if not self.get_current_user():
response = json.dumps({"userdetails":"my dummy user"})
self.write(response)
# user actually exists
else:
response = json.dumps({"userdetails":self.get_current_user()})
self.write(response)
I think you need to enable a cross origin request, wiki CORS for more info:
class BaseHandler(tornado.web.RequestHandler):
def set_default_headers(self):
self.set_header("Access-Control-Allow-Origin", "http://yoursite.com")
Also, it took me a while to figure this out, but normal sessions don't work when Angular is interacting with a RESTful API. What you want is to send credentials in the HTTP Authorisation header on each request. Check this out:
http://wemadeyoulook.at/en/blog/implementing-basic-http-authentication-http-requests-angular/
Hope that helps a bit!

Categories

Resources