From b17ed9b8c978dcc9f57e6f50b6670ae3c21cbccc Mon Sep 17 00:00:00 2001 From: lelo Date: Mon, 17 Mar 2025 22:05:57 +0000 Subject: [PATCH] add analytics --- access_log.db | Bin 0 -> 20480 bytes app.py | 74 +++++++++++++++++++++++++++++++++++++- docker-compose.yml | 2 -- templates/access_log.html | 46 ++++++++++++++++++++++++ 4 files changed, 119 insertions(+), 3 deletions(-) create mode 100644 access_log.db create mode 100644 templates/access_log.html diff --git a/access_log.db b/access_log.db new file mode 100644 index 0000000000000000000000000000000000000000..77531b682616e2a8c164701686a033bb15a86d69 GIT binary patch literal 20480 zcmeI3O>^2t7=R`GfrDc^i4UFWrQ1Vi?6EDazQMVari|nGqluxD`osvdMw;k@XrEAX7vuPf_<)D~CZU4_*@h1GxI0Sgj90!RP}AOR$R z1dsp{Kmter2_S(H0?!MJpKWc4r-|M28Gr3VnA+s*b;`o_cy;c{Nv+zbkw*2K!x|ZT z??z|`Y#Ywp>^~HX*^uXhG0!}oHSIQx#+!cB8LJjX#vJP;8zu5l@f@;$+^FrGQfx+i_y} z$T^)UXK~Z+urSH#jM*8BV-|nty7(-cS%5FD{g;tn7(9�V_xV2_OL^fCP{L5 z9NWn7D2x9~!h2!$x0U&fNP#6B<9I(&wd;P_$x(N$f&_Wf7 zR-~+*)T1y=?2b$|@;qja*Gb3@sim+XgVfuuA0-_-Ww8WTP%Kqa$`X~-qv(z2`?hRI zlx)28LMQ5Ra@-&amFQyv4W|B>{AB8zq}uKJ>?La*c!_LimZX_vA9NuIcI6fjxQbQm6Pq;8+tu{S)W=5#Lm+$8DpU0JrfU8%(q*G~Bk|L{d6hsoI% zlu-&_sdSV>){sLYYaXyTFWv*{eKDW9R*^JIS1e;D)Kx$bFfZh=--nY!k2!$9&O(Qg zG~}e7#DD)4FycUGoqomn6NY?aI{1^NDm*CC?a7`8GI1F?kC6 zg11Qz+)xsDZI`rSJ8iq{wj1aiRG^2hz=F0Ru70m@p`7xCqpFrV*+}PHajJ0ikHUm+ zS?GaZ>Vc;eC%4=+%HeP=d2tGP&6E^HF|^6fY0edq{6IZiLq3cDm*xw?>hCM!a<3#V z^@`%cAJBjW2_S(xK;V4wQKox;y^4{*(ApFJa=S|xr~J1c_ie=C~h&2 zMBtNnz8yNZ+z^iB3_%f3&z1H0!wVxglA*j1glbv_#zwQvQ1C`pDRLt4LMi`M^ zMx<*%eNtXn*Q9dAtYAil-h76>&sX#3|6pANBvGzdQzFt49f#{8gY}_nb^Up9<(m0# zKstUlS_dp)c*(Yv7}Z2 literal 0 HcmV?d00001 diff --git a/app.py b/app.py index d5fcb7e..55d79e1 100755 --- a/app.py +++ b/app.py @@ -4,7 +4,8 @@ from PIL import Image import io from functools import wraps import mimetypes -from datetime import datetime, date +import sqlite3 +from datetime import datetime, date, timedelta from urllib.parse import unquote import diskcache import json @@ -193,6 +194,75 @@ def api_browse(subpath): 'files': files }) +@app.route("/access-log") +@require_secret +def access_log(): + # Get timeframe filter from query parameter; default to "today" + timeframe = request.args.get('timeframe', 'today') + now = datetime.now() + + # Determine the start time based on the requested timeframe + if timeframe == 'today': + # Beginning of today + start = now.replace(hour=0, minute=0, second=0, microsecond=0) + elif timeframe == '7days': + start = now - timedelta(days=7) + elif timeframe == '30days': + start = now - timedelta(days=30) + elif timeframe == '365days': + start = now - timedelta(days=365) + else: + # Default to today if an unknown timeframe is passed + start = now.replace(hour=0, minute=0, second=0, microsecond=0) + + # Query the access log database for file access counts since the 'start' time + conn = sqlite3.connect('access_log.db') + cursor = conn.cursor() + cursor.execute(''' + SELECT full_path, COUNT(*) as access_count + FROM file_access_log + WHERE timestamp >= ? + GROUP BY full_path + ORDER BY access_count DESC + ''', (start.isoformat(),)) + rows = cursor.fetchall() + conn.close() + + return render_template("access_log.html", rows=rows, timeframe=timeframe) + +def log_file_access(full_path): + """ + Log file access details to a SQLite database. + Records the timestamp, full file path, client IP, user agent, and referrer. + """ + # Connect to the database (this will create the file if it doesn't exist) + conn = sqlite3.connect('access_log.db') + cursor = conn.cursor() + # Create the table if it doesn't exist + cursor.execute(''' + CREATE TABLE IF NOT EXISTS file_access_log ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + timestamp TEXT, + full_path TEXT, + ip_address TEXT, + user_agent TEXT, + referrer TEXT + ) + ''') + # Gather information from the request + timestamp = datetime.now().isoformat() + ip_address = request.remote_addr + user_agent = request.headers.get('User-Agent') + referrer = request.headers.get('Referer') + + # Insert the access record into the database + cursor.execute(''' + INSERT INTO file_access_log (timestamp, full_path, ip_address, user_agent, referrer) + VALUES (?, ?, ?, ?, ?) + ''', (timestamp, full_path, ip_address, user_agent, referrer)) + conn.commit() + conn.close() + @app.route("/media/") @require_secret def serve_file(filename): @@ -203,6 +273,8 @@ def serve_file(filename): app.logger.error(f"File not found: {full_path}") return "File not found", 404 + log_file_access(full_path) + mime, _ = mimetypes.guess_type(full_path) mime = mime or 'application/octet-stream' diff --git a/docker-compose.yml b/docker-compose.yml index 6b78c95..00c2e96 100755 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,5 +1,3 @@ -version: "3" - services: flask-app: image: python:3.11-slim diff --git a/templates/access_log.html b/templates/access_log.html new file mode 100644 index 0000000..271ae14 --- /dev/null +++ b/templates/access_log.html @@ -0,0 +1,46 @@ + + + + + File Access Log + + + +

File Access Log ({{ timeframe }})

+ +
+ + + + + + + + + {% for row in rows %} + + + + + {% else %} + + + + {% endfor %} + +
File PathAccess Count
{{ row[0] }}{{ row[1] }}
No data available for the selected timeframe.
+
+ + \ No newline at end of file