247 lines
4.7 KiB
Python
247 lines
4.7 KiB
Python
#!/usr/bin/env python3
|
||
|
||
import subprocess
|
||
import json
|
||
import time
|
||
import threading
|
||
from datetime import datetime
|
||
from pathlib import Path
|
||
|
||
from flask import Flask, jsonify, render_template_string
|
||
|
||
DB_DATA = Path("page_counts.json")
|
||
INTERVAL = 15 * 60 # 15 Minuten
|
||
|
||
app = Flask(__name__)
|
||
|
||
HTML = """
|
||
<!DOCTYPE html>
|
||
<html lang="de">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>JonasSearch – Pages</title>
|
||
|
||
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
||
|
||
<style>
|
||
body {
|
||
margin: 0;
|
||
background: #111;
|
||
color: #eee;
|
||
font-family: Arial, sans-serif;
|
||
}
|
||
|
||
.container {
|
||
max-width: 1200px;
|
||
margin: auto;
|
||
padding: 30px;
|
||
}
|
||
|
||
h1 {
|
||
margin-bottom: 5px;
|
||
}
|
||
|
||
.current {
|
||
font-size: 2.5rem;
|
||
margin: 20px 0;
|
||
}
|
||
|
||
.chart-container {
|
||
background: #1b1b1b;
|
||
padding: 20px;
|
||
border-radius: 15px;
|
||
}
|
||
</style>
|
||
</head>
|
||
|
||
<body>
|
||
<div class="container">
|
||
<h1>JonasSearch</h1>
|
||
<div>Indexed Pages</div>
|
||
|
||
<div class="current">
|
||
<span id="current">–</span> Pages
|
||
</div>
|
||
|
||
<div class="chart-container">
|
||
<canvas id="chart"></canvas>
|
||
</div>
|
||
</div>
|
||
|
||
<script>
|
||
let chart = null;
|
||
|
||
async function update() {
|
||
const response = await fetch("/api/data");
|
||
const data = await response.json();
|
||
|
||
document.getElementById("current").textContent =
|
||
data.values.length
|
||
? data.values[data.values.length - 1].count.toLocaleString("de-DE")
|
||
: "–";
|
||
|
||
const labels = data.values.map(x =>
|
||
new Date(x.timestamp).toLocaleString("de-DE")
|
||
);
|
||
|
||
const values = data.values.map(x => x.count);
|
||
|
||
if (chart) {
|
||
chart.data.labels = labels;
|
||
chart.data.datasets[0].data = values;
|
||
chart.update();
|
||
return;
|
||
}
|
||
|
||
chart = new Chart(document.getElementById("chart"), {
|
||
type: "line",
|
||
|
||
data: {
|
||
labels: labels,
|
||
|
||
datasets: [{
|
||
label: "Pages",
|
||
data: values,
|
||
tension: 0.25,
|
||
pointRadius: 2,
|
||
fill: true
|
||
}]
|
||
},
|
||
|
||
options: {
|
||
responsive: true,
|
||
|
||
scales: {
|
||
x: {
|
||
ticks: {
|
||
color: "#aaa"
|
||
}
|
||
},
|
||
|
||
y: {
|
||
beginAtZero: false,
|
||
ticks: {
|
||
color: "#aaa"
|
||
}
|
||
}
|
||
},
|
||
|
||
plugins: {
|
||
legend: {
|
||
labels: {
|
||
color: "#eee"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
});
|
||
}
|
||
|
||
update();
|
||
|
||
// Browser alle 30 Sekunden aktualisieren
|
||
setInterval(update, 30000);
|
||
</script>
|
||
|
||
</body>
|
||
</html>
|
||
"""
|
||
|
||
|
||
def load_data():
|
||
if not DB_DATA.exists():
|
||
return []
|
||
|
||
try:
|
||
with DB_DATA.open("r", encoding="utf-8") as f:
|
||
return json.load(f)
|
||
except Exception:
|
||
return []
|
||
|
||
|
||
def save_data(data):
|
||
tmp = DB_DATA.with_suffix(".tmp")
|
||
|
||
with tmp.open("w", encoding="utf-8") as f:
|
||
json.dump(data, f, indent=2)
|
||
|
||
tmp.replace(DB_DATA)
|
||
|
||
|
||
def get_page_count():
|
||
command = [
|
||
"sudo",
|
||
"-u",
|
||
"postgres",
|
||
"psql",
|
||
"-p",
|
||
"25432",
|
||
"-d",
|
||
"jonassearch",
|
||
"-t",
|
||
"-A",
|
||
"-c",
|
||
"SELECT COUNT(*) FROM pages;"
|
||
]
|
||
|
||
result = subprocess.run(
|
||
command,
|
||
capture_output=True,
|
||
text=True,
|
||
check=True
|
||
)
|
||
|
||
return int(result.stdout.strip())
|
||
|
||
|
||
def collect():
|
||
while True:
|
||
try:
|
||
count = get_page_count()
|
||
|
||
entry = {
|
||
"timestamp": datetime.now().isoformat(),
|
||
"count": count
|
||
}
|
||
|
||
data = load_data()
|
||
data.append(entry)
|
||
|
||
save_data(data)
|
||
|
||
print(
|
||
f"[{entry['timestamp']}] "
|
||
f"Pages: {count}"
|
||
)
|
||
|
||
except Exception as e:
|
||
print(f"Fehler beim Abrufen der Seitenzahl: {e}")
|
||
|
||
time.sleep(INTERVAL)
|
||
|
||
|
||
@app.route("/")
|
||
def index():
|
||
return render_template_string(HTML)
|
||
|
||
|
||
@app.route("/api/data")
|
||
def api_data():
|
||
return jsonify({
|
||
"values": load_data()
|
||
})
|
||
|
||
|
||
if __name__ == "__main__":
|
||
# Collector im Hintergrund starten
|
||
thread = threading.Thread(target=collect, daemon=True)
|
||
thread.start()
|
||
|
||
# Webserver
|
||
app.run(
|
||
host="0.0.0.0",
|
||
port=8081,
|
||
threaded=True
|
||
)
|