Fetches the location of the IP address that has requested to Nginx web server, then sends the relevant information via Gmail.
import json
from datetime import datetime
from file_watcher import file_watcher
from location_fetcher import location_fetcher
from gmail_sender import gmail_sender

def main():
    with open("configs.json", "r") as config_file:
        configs = json.load(config_file)
   
    wanted_country = configs["general"]["wanted_country"]
    limit = configs["general"]["limit_to_send_mail"]

    file_watcher_config = configs["file_watcher"]
    ip_fetcher_config = configs["ip_fetcher"]
    gmail_sender_config = configs["gmail_sender"]

    _file_watcher = file_watcher.FileWatcher(configs=file_watcher_config)
    _location_fetcher = location_fetcher.LocationFetcher(configs=ip_fetcher_config)
    _gmail_sender = gmail_sender.GmailSender(configs=gmail_sender_config)
    
    outside_of_country_list = []
    is_running = True

    while is_running:
        try:
            # Watching the file
            ip_address, timestamp, user_agent = _file_watcher.watch_file()
            # If data arrives, fetch the location
            data = _location_fetcher.fetch_location(ip_address)
            # If data is empty, continue to watch file
            if not data:
                continue

            country, city, lat, lon, data_to_send_mail = data

            # Formatting the timestamp and user_agent to increase readability
            timestamp = datetime.strptime(timestamp, "%d/%b/%Y:%H:%M:%S %z")
            timestamp = timestamp.strftime("%Y-%m-%d %H:%M:%S %Z")
            user_agent = user_agent.replace('"', '')
            
            if wanted_country and country != wanted_country:
                outside_of_country_list.append(data_to_send_mail)
                if len(outside_of_country_list) >= limit:
                    # TODO: Format data to send
                    message_subject = f"Summary of Last {limit} Requests"
                    message_body = "\n".join([f"{c}- {str(data)}\nTimestamp: {timestamp}\nUser Agent: {user_agent}" for c, data in enumerate(outside_of_country_list)])
                    outside_of_country_list = []
                else:
                    continue
            else:
                message_subject = f"{city} - {lat}, {lon}"
                message_body = f"Timestamp: {timestamp}\nUser Agent: {user_agent}\n{data_to_send_mail}"
            
            _gmail_sender.send_message(message_subject, message_body)
        
        except KeyboardInterrupt:
            is_running = False    
        except Exception as e:
            print(e)
            continue

if __name__ == "__main__":
    main()

app.py
senceryucel/nginx-ip-location-fetcher-sender
import time, re
class FileWatcher:
    # TODO: config options
    def __init__(self, configs) -> None:
        self.logfile = open('new_access.log', "r")

        # Going to the end of the file
        line = self.logfile.readlines()
        while line:
            line = self.logfile.readlines()

        # Setting the pattern to match   
        self.request_pattern = r'(\d+\.\d+\.\d+\.\d+) - - \[(.*?)\] "GET .*" 200 \d+ ".*" "(.*)"'
    
    
    def watch_file(self):
        while True:
            line = self.logfile.readline()
            match = re.match(self.request_pattern, line)
            # If the line matches with the regex, return
            if match:
                print(line)
                ip_address = match.group(1)
                timestamp = match.group(2)
                user_agent = match.group(3)
                return ip_address, timestamp, user_agent
            else:
                time.sleep(5)

file_watcher/file_watcher.py
senceryucel/nginx-ip-location-fetcher-sender
import os.path

from google.auth.transport.requests import Request
from google.oauth2.credentials import Credentials
from google_auth_oauthlib.flow import InstalledAppFlow
from googleapiclient.discovery import build
import base64
from email.mime.text import MIMEText

class GmailSender:
    def __init__(self, configs) -> None:
        self.SCOPES = ['https://www.googleapis.com/auth/gmail.send']
        self.mail_from = configs["mail_from"]
        self.mail_to = configs["mail_to"]

    # Authorizing
    def auth(self):
        if os.path.exists('gmail_sender/token.json'):
            creds = Credentials.from_authorized_user_file('gmail_sender/token.json', self.SCOPES)
            return creds
        
    # Creating mail content
    def create_message(self, sender, to, subject, message_text):
        message = MIMEText(message_text)
        message['to'] = to
        message['from'] = sender
        message['subject'] = subject
        return {'raw': base64.urlsafe_b64encode(message.as_string().encode()).decode()}


    # Sending the mail
    def send_message(self, message_subject, message):
        creds = self.auth()    
        service = build('gmail', 'v1', credentials=creds)
        message = self.create_message(self.mail_from, self.mail_to, message_subject, message)
        
        try:
            message = (service.users().messages().send(userId='me', body=message)
                    .execute())
            print(f"-----Mail sent to {self.mail_to}-----")
            return message
        except Exception as error:
            print(error)
gmail_sender/gmail_sender.py
senceryucel/nginx-ip-location-fetcher-sender
import requests
import json

class LocationFetcher:
    def __init__(self, configs) -> None:
        _generated_fields = configs["generated_fields"]
        self.generated_fields = [] 
        # Generating fields to fetch from API
        for field in _generated_fields.keys():
            if _generated_fields[field]:
                self.generated_fields.append(field)
        self.fields = ""
        for c, field in enumerate(self.generated_fields):
            if c == len(self.generated_fields) - 1:
                self.fields += field
            else:
                self.fields += f"{field},"
        print(self.fields)


    def fetch_location(self, ip):
        resp = requests.get(f"http://ip-api.com/json/{ip}?fields={self.fields}")
        
        # If request is not successful, it returns an empty list and throws a json exception
        try:
            data = json.loads(resp.content.decode())
        except json.decoder.JSONDecodeError:
            return False
        
        # For mail subject
        city = data["city"]
        lat = data["lat"]
        lon = data["lon"]
        _key = ""
        data_to_send_mail = ""
        for key in data.keys():
            if key == "status":
                continue
            elif key == "continent":
                _key = "Continent"
            elif key == "continentCode":
                _key = "Continent Code"
            elif key == "country":
                _key = "Country"
            elif key == "countryCode":
                _key = "Country Code"
            elif key == "region":
                _key = "Region"
            elif key == "regionName":
                _key = "Region Name"
            elif key == "city":
                _key = "City"
            elif key == "zip":
                _key = "Zip"
            elif key == "district":
                _key = "District"
            elif key == "lat":
                _key = "Latitude"
            elif key == "lon":
                _key = "Longitude"
            elif key == "timezone":
                _key = "Timezone"
            elif key == "offset":
                _key = "Offset"
            elif key == "isp":
                _key = "ISP"
            elif key == "org":
                _key = "Organization"
            elif key == "as":
                _key = "AS"
            elif key == "asname":
                _key = "AS Name"
            elif key == "reverse":
                _key = "Reverse"
            elif key == "mobile":
                _key = "Mobile"
            elif key == "proxy":
                _key = "Proxy"
            elif key == "hosting":
                _key = "Hosting"
            elif key == "query":
                _key = "IP Address"
            
            data_to_send_mail += f"{str(_key)} ----> {data[key]}\n"

        return data["countryCode"], city, lat, lon, data_to_send_mail
    
            
        
    
location_fetcher/location_fetcher.py
senceryucel/nginx-ip-location-fetcher-sender