102 lines
2.9 KiB
Python
102 lines
2.9 KiB
Python
"""
|
|
Auto Publisher — Analytics collector endpoint.
|
|
Lightweight Flask app that runs on each site CT to collect pageview data.
|
|
"""
|
|
from flask import Flask, request, jsonify
|
|
import sqlite3
|
|
import json
|
|
import hashlib
|
|
import time
|
|
from datetime import datetime, timedelta
|
|
from pathlib import Path
|
|
|
|
app = Flask(__name__)
|
|
|
|
DB_PATH = "/var/lib/auto-publisher/analytics.db"
|
|
|
|
|
|
def get_db():
|
|
Path(DB_PATH).parent.mkdir(parents=True, exist_ok=True)
|
|
db = sqlite3.connect(DB_PATH)
|
|
db.execute("""
|
|
CREATE TABLE IF NOT EXISTS pageviews (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
path TEXT NOT NULL,
|
|
vertical TEXT NOT NULL,
|
|
referrer TEXT DEFAULT '',
|
|
user_agent TEXT DEFAULT '',
|
|
ip_hash TEXT DEFAULT '',
|
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
)
|
|
""")
|
|
db.execute("""
|
|
CREATE INDEX IF NOT EXISTS idx_pageviews_path ON pageviews(path);
|
|
""")
|
|
db.execute("""
|
|
CREATE INDEX IF NOT EXISTS idx_pageviews_created ON pageviews(created_at);
|
|
""")
|
|
db.commit()
|
|
return db
|
|
|
|
|
|
@app.route("/a/collect", methods=["POST", "GET"])
|
|
def collect():
|
|
"""Collect a pageview ping."""
|
|
path = request.args.get("p", "/")
|
|
vertical = request.args.get("v", "unknown")
|
|
ref = request.args.get("r", "")
|
|
ua = request.headers.get("User-Agent", "")[:200]
|
|
ip = request.remote_addr or ""
|
|
ip_hash = hashlib.sha256(ip.encode()).hexdigest()[:16] if ip else ""
|
|
|
|
db = get_db()
|
|
db.execute(
|
|
"INSERT INTO pageviews (path, vertical, referrer, user_agent, ip_hash) VALUES (?, ?, ?, ?, ?)",
|
|
(path, vertical, ref, ua, ip_hash)
|
|
)
|
|
db.commit()
|
|
db.close()
|
|
|
|
# Return a 1x1 transparent GIF
|
|
return b"\x47\x49\x46\x38\x39\x61\x01\x00\x01\x00\x80\x00\x00\xff\xff\xff\x00\x00\x00\x21\xf9\x04\x01\x00\x00\x00\x00\x2c\x00\x00\x00\x00\x01\x00\x01\x00\x00\x02\x02\x44\x01\x00\x3b", 200, {
|
|
"Content-Type": "image/gif",
|
|
"Cache-Control": "no-cache, no-store, must-revalidate",
|
|
}
|
|
|
|
|
|
@app.route("/a/stats")
|
|
def stats():
|
|
"""Get site analytics summary."""
|
|
db = get_db()
|
|
db.row_factory = sqlite3.Row
|
|
|
|
today = datetime.now().strftime("%Y-%m-%d")
|
|
|
|
total = db.execute("SELECT COUNT(*) as c FROM pageviews").fetchone()["c"]
|
|
today_views = db.execute(
|
|
"SELECT COUNT(*) as c FROM pageviews WHERE created_at >= ?", (today,)
|
|
).fetchone()["c"]
|
|
|
|
top_pages = db.execute("""
|
|
SELECT path, COUNT(*) as c FROM pageviews
|
|
WHERE created_at >= date('now', '-30 days')
|
|
GROUP BY path ORDER BY c DESC LIMIT 10
|
|
""").fetchall()
|
|
|
|
db.close()
|
|
|
|
return jsonify({
|
|
"total_views": total,
|
|
"today_views": today_views,
|
|
"top_pages": [{"path": r["path"], "views": r["c"]} for r in top_pages],
|
|
})
|
|
|
|
|
|
@app.route("/health")
|
|
def health():
|
|
return jsonify({"status": "ok"})
|
|
|
|
|
|
if __name__ == "__main__":
|
|
app.run(host="0.0.0.0", port=5199, debug=False)
|