Bilal's Blog

Home - Blog - Updates

Pinged Whenever I Get a Visitor

I am determined that a lot can be done with Bash, specially when it comes to managing my website and automating the boring stuff. I am also determined on keeping my site super lite, and static of course. I don't want any javascript bullshit, just plain html and simple css. And just like Linkedin, I want to know who visits my website. You see, I have to admit that I like some attention. I also like knowing if someone checked out my resume. It might be a potential recruiter.

To run my site, I am using NGINX, an open source software for web serving, load balancing, and much more. I am however only using it as a web server, as that is all I really need to serve simple html. The other day I was ssh'd into my server, and was in the /var/log/ directory, as one does, going over my mail logs, authentication logs and other stuff, when I noticed a nginx/ directory. Inside of that directory, there were multiple log files, including a log file names access.log. Upon further inspection, I found out that this file contains a log of every visit to my website. It keeps a log of every ip address that connects to my website. And so I had this idea of getting notified somehow everytime this file gets modified, i.e., a new visit. And that is when the Bash wizard in me was awoken.

Just a disclaimer that the IP address I read is your router's public IP, meaning every device on your network that tries to connect to my website will have the same public IP, which is your router's IP address. Regarding geolocation, the public IP only reveals what city it's coming from, and nothing more.

cp -f /var/log/nginx/access.log /tmp/
ip_file=$(cat /tmp/access.log)
currentip=$(tail -n 1 <<< $ip_file | cut -d' ' -f1)

Here, I start by copying the log file to /tmp/ directory, reading the last line of that file, then storing the first field in the variable $currentip. Now I can append that IP to this API, and extract the city, state and country of that IP, like so.

location=$(curl -s http://ip-api.com/json/$currentip)
city=$(jq -r '.city' <<< $location)
region=$(jq -r '.region' <<< $location)
country=$(jq -r '.country' <<< $location)

Here, I run curl silently, and have its output be stored, which is in json format, be stored in variable $location. And to avoid using extra regex, I use jq to extract the entries needed in the json output, storing the city in $city, and so on. Lastly, I need my server to email me the output in a readable format, which I do here.

echo "$city, $region, $country" | mail -s "New Visit: $currentip" bilal@bilalhalim.xyz

This will mail me the city, state, and country to my email. I also need to invoke the script everytime the log file access.log gets modified. This is where the magic on entr comes to play. I have linked to its GitHub, as I will not go into detail on how it works. But basically, it lets me run my script everytime the log file gets modified. I can do this with this command

ls /var/log/nginx/access.log | entr ./myscript.sh

And now for the final script, you can copy this and run this on your webserver. But obviously put in your email instead of mine LOL.

#!/bin/bash
cp -f /var/log/nginx/access.log /tmp/
ip_file=$(cat /tmp/access.log)
currentip=$(tail -n 1 <<< $ip_file | cut -d' ' -f1)

[[ -f /tmp/last_ip.log ]] && lastip=$(cat /tmp/last_ip.log)

if [ "$currentip" == "$lastip" ]; then
	exit;
else

	location=$(curl -s http://ip-api.com/json/$currentip)
	city=$(jq -r '.city' <<< $location)
	region=$(jq -r '.region' <<< $location)
	country=$(jq -r '.country' <<< $location)
	echo "$city, $region, $country" | mail -s "New Visit: $currentip" bilal@bilalhalim.xyz
fi

echo $currentip > /tmp/last_ip.log
--------------------------------------------------------