Python Streamlit running on Heroku - how to get remote/client ip? - python

I was able to deploy an Streamlit App to Heroku. I did that to compare Heroku and Streamlit Share. Both runs fine, but I want to log client's IP (remote IP) to understand where my app is getting accessed from.
I'm googling for two days now without success. I saw a lot of posts related to the subject, most discussing how to get the real IP, where is it HTTP_X_FORWARDED_FOR and so on.
At first I understood that ENV['HTTP_X_FORWARDED_FOR'] was a environment var which I could get and parse using os.environ['HTTP_X_FORWARDED_FOR'] but it is not the case. I even tried request.headers['X-Forwarded-For'] which requires Flask but I learnt that Flask and Streamlit do not get along. Other solutions dealing with PHP/Java are beyond my knowledge.
So, my question is: "Is it possible to get remote IP using a Python package, without Flask?"

You can make the client inform the IP address to the host by using the streamlit-javascript package and inserting a JavaScript code that access an external service, e.g. api.ipify.org.
import streamlit as st
from streamlit_javascript import st_javascript
def client_ip():
url = 'https://api.ipify.org?format=json'
script = (f'await fetch("{url}").then('
'function(response) {'
'return response.json();'
'})')
try:
result = st_javascript(script)
if isinstance(result, dict) and 'ip' in result:
return result['ip']
except:
pass
ip_address = client_ip() # now you have it in the host...
st.write(ip_address) # so you can log it, etc.
It's not a perfect solution (as it won't be of any use inside a NAT, for instance), but I think it's as far as you can go with Streamlit.
Of course, acquiring the request IP from Tornado (the webserver) could be a Streamlit feature, but it isn't just yet.

Related

How do I deploy my Python Flask dev app so other devices across the network (Wifi) can also access it?

This is my app.py file:
from flask import Flask
app = Flask(__name__)
#app.route('/')
def hello():
return "Hello World!"
app.run(host = '192.168.2.18', debug=False)
I've used the netstat command in Windows and that's how I know to use '192.168.2.18' When I run the file, it appears to run on port 5000
Please see pic below:
However, when I try to access this url from another device over the network, I don't see it. I only see it on my machine.
I've tried various solutions most notably from this popular thread:
Configure Flask dev server to be visible across the network
If you update the host ip in your code to 0.0.0.0, then your app will start listening all the public IPs.
app.run(host='0.0.0.0')
You can read here in the flask docs.
Now for someone to access your page, they have to navigate to this url -
http://your.machine.ip:5000/
You can also give any specific port you want your application to run at, lets say 4000 (default port is 5000)
app.run(host='0.0.0.0', port=4000)
You can even give your machine name in place of your machine ip in the url, just incase your ip is not static.
I tell you a secret, run:
python3 -m http.server
and the directory in which you are will be available through all the network.
In this example I'm visiting my Pictures folder and as you can see is available at http://0.0.0.0:8000/
Since you have mentioned, you already tried the solutions from this post, here's something I think you can try because I was facing this same problem once and this is what I was missing, and it's overlooked a lot of times.
If you are a Windows user, check your Network and sharing settings, and turn it ON for sharing on a private network. Something like this:
Also, make sure your IP address in the code below is correct (because IP address changes all the time when you connect your laptop/computer over Wi-Fi), so check the current IP by running ipconfig in cmd right before running your code, not the one you wrote a week ago.
Few changes in the code:
from flask import Flask
app = Flask(__name__)
#app.route('/')
def hello():
return "Hello World!"
if __name__ == "__main__":
#app.run(host='0.0.0.0',port=5000) //doesn't work for me
app.run(host='192.168.2.18', port=5000)
And finally, run your code with flask run -h 192.168.X.X, in the screenshot you shared, you entered flask run --host 192.168.X.X, I m not sure why, but --h doesn't work for me.
And, I hope you also check you are entering the address properly, like http://192.168.2.18:5000, not https.
Sometimes silly stuff can get in the way too :|

Slack bot running python functions

I am creating a Slack Bot, I have successfully installed it, although I am not 100% sure what does ngrok do to run it when I have node server running locally. I have followed this guide.
I have a python script that:
Connects to a database
Downloads the entire table and creates a pandas dataframe
In the script, I have declared many variables which takes specific information from the dataframe.
For example:
monday_sales = sum((df['sale_event_time']>=week) & (df['sale_event_time']<=today_real) & ((df['sale_event_time'].apply(lambda x:x.isoweekday()))==1))
Which simply returns how many sales I had on Monday from the dataframe.
My goal is to connect slack bot to my python script, basically being able to run /monday_sales in slack which would print the number that monday_sales variable holds.
I haven't used much of node or js, so linking everything together I find a bit difficult.
First I run ngrok like so:
./ngrok http 4390
Then I run node server locally(?)
var http = require('http');
const PORT=4390;
function handleRequest(request, response){
response.end('My bot is ALIIIIIIVEEEEE');
}
var server = http.createServer(handleRequest);
server.listen(PORT, function(){
console.log("Server listening on: http://localhost:%s", PORT);
});
And when I run /ngrok in slack it prints out 'My bot is ALIIIIIIVEEEEE, although only visible to me.
So the question could be split in to few:
Why do I need ngrok and node.js server running locally?
How can I trigger python functions / variables from slack bot.
Is there a better way of having slack bot only ran by python without node, ngrok?
Thank you very much for your suggestions.
I think your best bet is to ditch this tutorial and Node, as it seems you're only using it as a ngrok wrapper. Instead, since the rest of your bot is in Python, just use pyngrok (full disclosure, I am the developer), which is a Python wrapper for ngrok. It gives you programmatic access to the tunnels and everything else.
If this solves your use case, you can check out the docs here.

Python Flask can't host Backend on my machine

So I wanted to host my backed on my machine for my website.
The Problem is I can't the frontend works perfectly but the Flask backend doesn't.
The backend works when I use localhost or my local ip to access it.
But doesn't work when I try to use it with the link :
https://desktop-1234567.1234567890qwert.myfritz.net:3182
(this one is fake)
It displays this error message :
So basically it says that it can't find the backend the Flask backend also dosen't react.
Even though I port forwarded the port 3182 with my-Fritz :
I also turned off Windows Firewall and it dosen't help
My Fritz is basically a free service from avm for Firtz-Box users (that's my Router)
It generates a sub domain where all your traffic is routed through this is useful since this url is static.
I tried it with a simpler backend :
from flask import Flask
app = Flask(__name__)
#app.route("/test")
def test():
return "<h1>Test</h1>"
if __name__ == "__main__":
app.run(
debug=False,
host='0.0.0.0',
port=3182,
threaded=True
)
and it also doesn't work it gives the same error message as above.
I also tried using waitress to host this simple app but it also won't work.
It has to be Flask since I can access my Frontend with the link on port 80 and 443.
I'm on Windows 10 and I hope somebody knows why this isn't working.
So yeah pleas help me I searched and it appears that no one had this exact problem before (But you never know on Stack Overflow)
Well I found out what the issue is.
The Issue is that I used "0.0.0.0" as the host parameter to run the app.
This tells flask to use IPV4, however I looked in to it and found out that "My Fritz" uses IPV6 so I just had to change the host parameter to use IPV6 by replacing "0.0.0.0" with ::.
That's it hope it helps, if anyone runs into the same issue as me.

Cannot connect to RabbitMQ on Heroku with pika due to ProbableAccessDeniedError

I just set up a RabbitMQ add-on in heroku. After developing my app to queue up and consume messages running on a local instance, I deployed it to Heroku and have not been able to connect successfully yet. The username/password & hostname/port/vhost are all from heroku config. If I change the username or password, the error changes to ProbableAuthenticationError which makes me believe the authentication is at least correct, but likely an issue with my vhost or some other missing configuration. I haven't seen any similar questions on SO or after an hour of Googling that didn't address my issue.
I have tried both the RABBITMQ_BIGWIG_RX_URL and RABBITMQ_BIGWIG_TX_URL environment variables for both sending and consuming, and no combination seems to work. Below is the code I have for attempting to connect.
url = 'small-laurel-24.bigwig.lshift.net'
port = 10019
vhost = '/notmyrealvhost'
credentials = pika.PlainCredentials('username', 'password')
parameters = pika.ConnectionParameters(url, port, vhost, credentials=credentials)
connection = pika.BlockingConnection(parameters)
Is there something I'm missing or any way to figure out what specifically is configured wrong? I'm at a loss here. Much thanks in advance!
I am running pika 0.9.14, python 2.7.3.
The problem was most likely that you added the forward slash character in your virtual-host. Many users confuse this with the forward slash being the root directory, but it is actually just the default virtual-host name.
Unless you actually named the virtual-host using a forward slash, the name will always be identical to the name you see in the management console, e.g:
my_virtualhost and not /my_virtualhost
This is why your solution worked as you did not add the extra forward slash when using URLParameters.
Your original code would have looked like this using URLParameters:
amqp://username:password#small-laurel-24.bigwig.lshift.net:10018/%2Fnotmyrealvhost
While the working version you mentioned in your answer above does not have the forward slash (%2F) character.
amqp://username:password#small-laurel-24.bigwig.lshift.net:10018/notmyrealvhost
I ended up solving my problem by using the URLParameters class on pika to parse the URL from Heroku's environment variable.
This takes the string like
amqp://username:password#small-laurel-24.bigwig.lshift.net:10018/notmyrealvhost and parses everything as needed. I was unnecessarily complicating things by doing it myself.

How to accept a JSON POST?

First time here, it says to be specific... So here goes.
I'm doing a small project to connect Salesforce to my Raspberry Pi. The goal is to make a light (Think a beacon, siren-like light) flash when a high priority case comes in from a client in Salesforce. At the moment, clients usually send an email to a certain address, and this creates a case. It goes to the 'Unassigned Queue' and emails the team that this case is there waiting to be assigned.
Salesforce uses REST, so I need to be able to get the Pi to accept JSON so it can easily understand what Salesforce wants it to do.
Currently, I guess I have won half of the battle. I have a web server (Lighttpd) running on the Pi, which hosts an index page and a Python script. I am also using a Python wrapper, which allows me to easily run a command from a Telldus program I have installed. This program controls a USB RF Transmitter that I have connected, it is paired to a RF Socket, which is connected to the mains power supply with a light connected to it.
So the Python script is called power.py, and can be controlled with URL variables, so if I go to power.py?device=1&power=on&time=10&password=hunter2 that turns on device 1 for 10 seconds. I also created a POST form on the index page, which just POSTS to the python script, and runs it in the same way as using the URL variables. That all works great.
So all I need to do, is connect it to Salesforce. I would like to use REST and JSON, so that if I ever move away from Salesforce to another CRM program, it will easily be able to adapt and receive instructions from new places.
I have posted the Python script I am using here:
https://github.com/7ewis/RemotePiControl/blob/master/power.py
The Pi isn't currently allowed out of the local network, so I will need to somehow develop a way to send JSON commands, and the recieve and convert them to work using the correct variables etc. I'm not a programmer, I've just exposure to languages from hacking things and exploring. Hence why I need some guidance with this.
I have never used REST or JSON before, so what would I need to do to achieve this?
Seems like adding Flask http://flask.pocoo.org to your Raspberry Pi Webserver would be a good move. It allows server-side python to be run in response to JQuery ajax (and regular) requests. Check out a couple of examples here:
http://flask.pocoo.org/docs/patterns/jquery/
and this stack overflow question: how can I use data posted from ajax in flask?
Flask is pretty straightforward to get up and running, and is happy working with a number of servers, including Lighttpd. Writing RESTful flask is also a perfectly reasonable proposition, see: http://blog.miguelgrinberg.com/post/designing-a-restful-api-with-python-and-flask
Additionally, lots of people have used flask on the raspberry pi already-- so that could help get you up and running smoothly: http://mattrichardson.com/Raspberry-Pi-Flask/
Good luck!
Firstly don't use a Python script that prints out result directly to CGI. You will be forever debugging it.
Use a light weight framework like Flask. You can do something as simple as
from flask import Flask
application = Flask(__name__)
#application.route('/', methods=['GET', 'POST'])
def index():
if request.method == 'POST':
# use Flask's build in json decoder
the_data = request.get_json()
# then do something with the data
return "This was a POST request, how interesting..."
else:
# request was GET rather than POST, so do something with else
return "Hello World!"
See how to configure Flask to run with Lighttpd here http://flask.pocoo.org/docs/deploying/fastcgi/
If you want to test this you can either write another Python script to send JSON data to your server (I recommend looking at the Python Requests library for this http://www.python-requests.org/en/latest/), or you can do this manually using a HTTP request builder, such as HTTPRequester for Firefox (https://addons.mozilla.org/en-US/firefox/addon/httprequester/)

Categories

Resources