haupt und konfig seite

This commit is contained in:
Matthias Czech 2025-03-17 22:34:46 +01:00
parent 0ee2a554a4
commit 8bc90158d9
1 changed files with 267 additions and 209 deletions

View File

@ -21,23 +21,34 @@ try {
exit; exit;
} }
// Bearer Token für Mastodon-API (falls benötigt hier nicht direkt in diesem File) // -----------------------
$bearerToken = 'your_bearer_token_here'; // AJAX-Anfragen (POST)
// -----------------------
// Falls eine AJAX-Anfrage vorliegt, wird über den Parameter "action" unterschieden. if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action'])) {
if (isset($_POST['action'])) {
$action = $_POST['action']; $action = $_POST['action'];
header('Content-Type: application/json'); header('Content-Type: application/json');
if ($action === 'save_hashtags') { if ($action === 'save_config') {
// Erwartet ein Array "hashtags" (JSON-dekodiert via AJAX) // Erwartet: from_date, to_date, refresh_interval
$from_date = $_POST['from_date']; // z.B. "2025-03-17T14:00"
$to_date = $_POST['to_date'];
$refresh_interval = $_POST['refresh_interval'];
// Aktualisieren wir gehen davon aus, dass die Config-Tabelle eine einzelne Zeile mit id=1 hat.
$stmt = $pdo->prepare("UPDATE config SET from_date = :from_date, to_date = :to_date, refresh_interval = :refresh_interval WHERE id = 1");
$stmt->execute([
'from_date' => date('Y-m-d H:i:s', strtotime($from_date)),
'to_date' => date('Y-m-d H:i:s', strtotime($to_date)),
'refresh_interval' => $refresh_interval
]);
echo json_encode(['status' => 'ok']);
exit;
} elseif ($action === 'save_hashtags') {
// Erwartet ein Array "hashtags" mit den Feldern: hashtag_id, hashtag, active
$hashtags = isset($_POST['hashtags']) ? $_POST['hashtags'] : []; $hashtags = isset($_POST['hashtags']) ? $_POST['hashtags'] : [];
$response = ['saved' => 0, 'updated' => 0]; $response = ['saved' => 0, 'updated' => 0];
foreach ($hashtags as $row) { foreach ($hashtags as $row) {
// Falls eine ID vorhanden ist, update ansonsten insert
if (isset($row['hashtag_id']) && !empty($row['hashtag_id'])) { if (isset($row['hashtag_id']) && !empty($row['hashtag_id'])) {
// Update vorhandener Eintrag // Update
$stmt = $pdo->prepare("UPDATE hashtags SET hashtag = :hashtag, active = :active WHERE hashtag_id = :hashtag_id"); $stmt = $pdo->prepare("UPDATE hashtags SET hashtag = :hashtag, active = :active WHERE hashtag_id = :hashtag_id");
$stmt->execute([ $stmt->execute([
'hashtag' => $row['hashtag'], 'hashtag' => $row['hashtag'],
@ -58,13 +69,10 @@ if (isset($_POST['action'])) {
echo json_encode($response); echo json_encode($response);
exit; exit;
} elseif ($action === 'save_accounts') { } elseif ($action === 'save_accounts') {
// Erwartet ein Array "accounts" (JSON) // Erwartet ein Array "accounts" (Felder: acc_id, active)
$accounts = isset($_POST['accounts']) ? $_POST['accounts'] : []; $accounts = isset($_POST['accounts']) ? $_POST['accounts'] : [];
$response = ['updated' => 0]; $response = ['updated' => 0];
foreach ($accounts as $row) { foreach ($accounts as $row) {
// Aktualisiere den active-Status in der Tabelle "users"
// Annahme: Tabelle "users" hat die Spalten: acc_id, username, active
$stmt = $pdo->prepare("UPDATE users SET active = :active WHERE acc_id = :acc_id"); $stmt = $pdo->prepare("UPDATE users SET active = :active WHERE acc_id = :acc_id");
$stmt->execute([ $stmt->execute([
'active' => !empty($row['active']) ? 1 : 0, 'active' => !empty($row['active']) ? 1 : 0,
@ -74,69 +82,109 @@ if (isset($_POST['action'])) {
} }
echo json_encode($response); echo json_encode($response);
exit; exit;
} elseif ($action === 'sync_accounts') { } elseif ($action === 'get_dashboard_data') {
// Synchronisation: Aus der findings-Tabelle werden alle getrackten Accounts (acc_id, username) ermittelt // Lese den Zeitraum und Refresh-Intervall aus der config-Tabelle (id=1)
// Für jeden Account, der noch nicht in der "users"-Tabelle existiert, erfolgt ein Insert mit active=0 $configStmt = $pdo->query("SELECT from_date, to_date, refresh_interval FROM config WHERE id = 1");
$stmt = $pdo->query("SELECT DISTINCT acc_id, username FROM findings"); $config = $configStmt->fetch();
$tracked = $stmt->fetchAll(); if (!$config) {
$config = ['from_date' => date('Y-m-d H:i:s', strtotime('-1 day')), 'to_date' => date('Y-m-d H:i:s'), 'refresh_interval' => 20];
foreach ($tracked as $acc) { }
// Prüfen, ob der Account bereits in der users-Tabelle existiert // Aggregation: Nur Beiträge innerhalb des Zeitraums, zu aktiven Hashtags und von aktiven Usern
$checkStmt = $pdo->prepare("SELECT COUNT(*) as cnt FROM users WHERE acc_id = :acc_id"); $query = "SELECT f.username, h.hashtag,
$checkStmt->execute(['acc_id' => $acc['acc_id']]); SUM(f.replies_count) AS total_replies,
$exists = $checkStmt->fetch(); SUM(f.reblogs_count) AS total_reblogs,
if ($exists['cnt'] == 0) { SUM(f.favorites_count) AS total_favorites
// Neuer Account unaktiv in die Tabelle schreiben FROM findings f
$insertStmt = $pdo->prepare("INSERT INTO users (acc_id, username, active) VALUES (:acc_id, :username, 0)"); JOIN hashtags h ON f.hashtag_id = h.hashtag_id
$insertStmt->execute([ JOIN users u ON f.acc_id = u.acc_id
'acc_id' => $acc['acc_id'], WHERE f.created_at BETWEEN :from_date AND :to_date
'username' => $acc['username'] AND h.active = 1
AND u.active = 1
GROUP BY h.hashtag, f.username
ORDER BY h.hashtag, f.username";
$stmt = $pdo->prepare($query);
$stmt->execute([
'from_date' => $config['from_date'],
'to_date' => $config['to_date']
]); ]);
$data = $stmt->fetchAll();
// Bestimme globale Maximalwerte für jeden Metrik, um die Balkenhöhen zu skalieren
$maxReplies = $maxReblogs = $maxFavorites = 0;
foreach ($data as $row) {
if ($row['total_replies'] > $maxReplies) { $maxReplies = $row['total_replies']; }
if ($row['total_reblogs'] > $maxReblogs) { $maxReblogs = $row['total_reblogs']; }
if ($row['total_favorites'] > $maxFavorites) { $maxFavorites = $row['total_favorites']; }
} }
} echo json_encode([
// Nun alle Accounts aus der users-Tabelle abrufen 'data' => $data,
$stmt = $pdo->query("SELECT acc_id, username, active FROM users ORDER BY username ASC"); 'max' => ['replies' => $maxReplies, 'reblogs' => $maxReblogs, 'favorites' => $maxFavorites]
$accountsList = $stmt->fetchAll(); ]);
echo json_encode($accountsList);
exit; exit;
} }
// Weitere Aktionen können hier ergänzt werden // Weitere Aktionen können ergänzt werden.
exit; exit;
} }
// Wenn kein "action" gesetzt ist, wird der HTML-Teil (Dashboard) ausgegeben. // -----------------------
// Der Konfigbereich ist über den Parameter "setup" erreichbar. // GET-Anfragen: Anzeige der Seite
?> // -----------------------
// Wenn action=setup in der URL übergeben wurde, wird der Konfigurationsbereich angezeigt.
if (isset($_GET['action']) && $_GET['action'] == 'setup') {
// Lese Konfiguration aus der config-Tabelle (angenommen, es gibt eine einzelne Zeile mit id=1)
$configStmt = $pdo->query("SELECT from_date, to_date, refresh_interval FROM config WHERE id = 1");
$config = $configStmt->fetch();
if (!$config) {
$config = ['from_date' => date('Y-m-d\TH:i'), 'to_date' => date('Y-m-d\TH:i'), 'refresh_interval' => 20];
} else {
// Für datetime-local Eingabefelder im Format "YYYY-MM-DDTHH:MM"
$config['from_date'] = date('Y-m-d\TH:i', strtotime($config['from_date']));
$config['to_date'] = date('Y-m-d\TH:i', strtotime($config['to_date']));
}
// Lade Hashtag-Konfiguration
$hashtagStmt = $pdo->query("SELECT hashtag_id, hashtag, active FROM hashtags ORDER BY hashtag ASC");
$hashtags = $hashtagStmt->fetchAll();
// Lade getrackte User: Alle unterschiedlichen User aus findings, links zu users (falls nicht vorhanden, erscheinen sie als inaktiv)
$userStmt = $pdo->query("SELECT DISTINCT f.acc_id, f.username, IFNULL(u.active, 0) as active
FROM findings f
LEFT JOIN users u ON f.acc_id = u.acc_id
ORDER BY f.username ASC");
$users = $userStmt->fetchAll();
?>
<!DOCTYPE html> <!DOCTYPE html>
<html lang="de"> <html lang="de">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<title>Dashboard & Konfiguration</title> <title>Dashboard Konfiguration</title>
<!-- jQuery einbinden -->
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script> <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<style> <style>
table { border-collapse: collapse; width: 100%; margin-bottom: 1em; } table { border-collapse: collapse; width: 100%; margin-bottom: 1em; }
th, td { border: 1px solid #ccc; padding: 5px; text-align: left; } th, td { border: 1px solid #ccc; padding: 5px; text-align: left; }
.error { color: red; } .error { color: red; }
.config-section { margin-bottom: 2em; padding: 1em; border: 1px solid #ddd; } .config-section { margin-bottom: 2em; padding: 1em; border: 1px solid #ddd; }
.config-section h2 { margin-top: 0; }
</style> </style>
</head> </head>
<body> <body>
<h1>Dashboard - Konfigurationsbereich</h1> <h1>Dashboard Konfiguration</h1>
<!-- Zeitraum Konfiguration -->
<div class="config-section" id="zeitraum-config"> <!-- Zeitraum-Konfiguration -->
<h2>Zeitraum definieren (von - bis)</h2> <div class="config-section">
<label for="von">Von:</label> <h2>Zeitraum</h2>
<input type="datetime-local" id="von" name="von"> <form id="config-form">
<label for="bis">Bis:</label> <label for="from_date">Von:</label>
<input type="datetime-local" id="bis" name="bis"> <input type="datetime-local" id="from_date" name="from_date" value="<?php echo $config['from_date']; ?>">
<label for="to_date">Bis:</label>
<input type="datetime-local" id="to_date" name="to_date" value="<?php echo $config['to_date']; ?>">
<label for="refresh_interval">Refresh Intervall (Sekunden):</label>
<input type="text" id="refresh_interval" name="refresh_interval" value="<?php echo $config['refresh_interval']; ?>" size="3">
<button type="button" id="save-config">Speichern</button>
</form>
</div> </div>
<!-- Hashtag Konfiguration --> <!-- Hashtag-Konfiguration -->
<div class="config-section" id="hashtag-config"> <div class="config-section">
<h2>Hashtags</h2> <h2>Hashtags</h2>
<table id="hashtag-table"> <table id="hashtag-config-table">
<thead> <thead>
<tr> <tr>
<th>Hashtag</th> <th>Hashtag</th>
@ -144,33 +192,22 @@ if (isset($_POST['action'])) {
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<?php <?php foreach($hashtags as $h): ?>
// Lade alle Hashtags aus der Datenbank (nur aktive ? oder alle) <tr data-id="<?php echo $h['hashtag_id']; ?>">
$stmt = $pdo->query("SELECT hashtag_id, hashtag, active FROM hashtags ORDER BY hashtag ASC"); <td><input type="text" class="hashtag-text" value="<?php echo htmlspecialchars($h['hashtag']); ?>"></td>
while ($row = $stmt->fetch()) { <td><input type="checkbox" class="hashtag-active" <?php echo ($h['active'] == 1) ? 'checked' : ''; ?>></td>
echo "<tr data-id=\"{$row['hashtag_id']}\">"; </tr>
echo "<td><input type=\"text\" class=\"hashtag-text\" value=\"" . htmlspecialchars($row['hashtag']) . "\"></td>"; <?php endforeach; ?>
$checked = ($row['active'] == 1) ? "checked" : "";
echo "<td><input type=\"checkbox\" class=\"hashtag-active\" $checked></td>";
echo "</tr>";
}
?>
</tbody> </tbody>
</table> </table>
<button id="add-hashtag">+1</button> <button id="add-hashtag">+1</button>
<button id="save-hashtags" disabled>Speichern</button> <button id="save-hashtags">Speichern</button>
</div> </div>
<!-- Account Konfiguration --> <!-- User-Konfiguration -->
<div class="config-section" id="account-config"> <div class="config-section">
<h2>Getrackte Accounts</h2> <h2>Getrackte User</h2>
<div> <table id="user-config-table">
<label for="sync-interval">Sync Intervall (Sekunden):</label>
<input type="text" id="sync-interval" value="30" size="3">
<label for="autosync">Autosync</label>
<input type="checkbox" id="autosync" checked>
</div>
<table id="account-table">
<thead> <thead>
<tr> <tr>
<th>Acc_ID</th> <th>Acc_ID</th>
@ -179,39 +216,42 @@ if (isset($_POST['action'])) {
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<!-- Die Tabelle wird per AJAX befüllt --> <?php foreach($users as $u): ?>
<tr data-id="<?php echo $u['acc_id']; ?>">
<td><?php echo $u['acc_id']; ?></td>
<td><?php echo htmlspecialchars($u['username']); ?></td>
<td><input type="checkbox" class="user-active" <?php echo ($u['active'] == 1) ? 'checked' : ''; ?>></td>
</tr>
<?php endforeach; ?>
</tbody> </tbody>
</table> </table>
<button id="save-accounts" disabled>Speichern</button> <button id="save-users">Speichern</button>
</div> </div>
<script> <script>
$(document).ready(function(){ $(document).ready(function(){
var hashtagChanged = false; // Konfigurationsspeicherung für Zeitraum
var accountChanged = false; $('#save-config').click(function(){
var autosyncTimer; $.ajax({
url: 'dashboard.php',
// Markiere Änderung in der Hashtag-Tabelle type: 'POST',
$('#hashtag-table').on('input change', 'input', function(){ dataType: 'json',
hashtagChanged = true; data: {
$('#save-hashtags').prop('disabled', false); action: 'save_config',
from_date: $('#from_date').val(),
to_date: $('#to_date').val(),
refresh_interval: $('#refresh_interval').val()
},
success: function(resp) {
alert('Konfiguration gespeichert.');
}
});
}); });
// +1 Button: Fügt eine neue Zeile ein (ohne ID, active standardmäßig false) // Hashtag-Konfiguration speichern
$('#add-hashtag').click(function(){
var newRow = '<tr data-id="">' +
'<td><input type="text" class="hashtag-text" value=""></td>' +
'<td><input type="checkbox" class="hashtag-active"></td>' +
'</tr>';
$('#hashtag-table tbody').append(newRow);
hashtagChanged = true;
$('#save-hashtags').prop('disabled', false);
});
// Speichern-Button für Hashtags
$('#save-hashtags').click(function(){ $('#save-hashtags').click(function(){
var hashtags = []; var hashtags = [];
$('#hashtag-table tbody tr').each(function(){ $('#hashtag-config-table tbody tr').each(function(){
var id = $(this).data('id'); var id = $(this).data('id');
var text = $(this).find('.hashtag-text').val().trim(); var text = $(this).find('.hashtag-text').val().trim();
var active = $(this).find('.hashtag-active').is(':checked') ? 1 : 0; var active = $(this).find('.hashtag-active').is(':checked') ? 1 : 0;
@ -225,48 +265,18 @@ if (isset($_POST['action'])) {
dataType: 'json', dataType: 'json',
data: { action: 'save_hashtags', hashtags: hashtags }, data: { action: 'save_hashtags', hashtags: hashtags },
success: function(resp){ success: function(resp){
alert('Hashtags gespeichert: Neu ' + resp.saved + ', Aktualisiert ' + resp.updated); alert('Hashtags gespeichert.');
hashtagChanged = false;
$('#save-hashtags').prop('disabled', true);
// Seite neu laden oder Tabelle aktualisieren
location.reload(); location.reload();
} }
}); });
}); });
// Funktion zum Laden der Accounts (sync) // User-Konfiguration speichern
function loadAccounts(){ $('#save-users').click(function(){
$.ajax({
url: 'dashboard.php',
type: 'POST',
dataType: 'json',
data: { action: 'sync_accounts' },
success: function(accounts){
var tbody = $('#account-table tbody');
tbody.empty();
$.each(accounts, function(i, acc){
var checked = (acc.active == 1) ? 'checked' : '';
tbody.append('<tr data-id="'+acc.acc_id+'"><td>'+acc.acc_id+'</td><td>'+acc.username+'</td><td><input type="checkbox" class="account-active" '+checked+'></td></tr>');
});
}
});
}
// Initiales Laden der Accounts
loadAccounts();
// Markiere Änderung in der Accounts-Tabelle
$('#account-table').on('change', 'input.account-active', function(){
accountChanged = true;
$('#save-accounts').prop('disabled', false);
});
// Speichern-Button für Accounts
$('#save-accounts').click(function(){
var accounts = []; var accounts = [];
$('#account-table tbody tr').each(function(){ $('#user-config-table tbody tr').each(function(){
var acc_id = $(this).data('id'); var acc_id = $(this).data('id');
var active = $(this).find('.account-active').is(':checked') ? 1 : 0; var active = $(this).find('.user-active').is(':checked') ? 1 : 0;
accounts.push({acc_id: acc_id, active: active}); accounts.push({acc_id: acc_id, active: active});
}); });
$.ajax({ $.ajax({
@ -275,40 +285,88 @@ if (isset($_POST['action'])) {
dataType: 'json', dataType: 'json',
data: { action: 'save_accounts', accounts: accounts }, data: { action: 'save_accounts', accounts: accounts },
success: function(resp){ success: function(resp){
alert('Accounts aktualisiert: ' + resp.updated + ' Einträge.'); alert('User gespeichert.');
accountChanged = false;
$('#save-accounts').prop('disabled', true);
} }
}); });
}); });
// Autosync: Prüfe Intervallfeld und starte einen Loop, wenn aktiviert
function startAutosync(){
clearInterval(autosyncTimer);
var interval = parseInt($('#sync-interval').val(), 10);
if(isNaN(interval) || interval <= 0){
$('#sync-interval').addClass('error');
interval = 30; // Standardwert
} else {
$('#sync-interval').removeClass('error');
}
autosyncTimer = setInterval(function(){
if($('#autosync').is(':checked')){
loadAccounts();
} else {
clearInterval(autosyncTimer);
}
}, interval * 1000);
}
// Starte Autosync beim Laden
startAutosync();
// Ändert sich das Intervall, neu starten
$('#sync-interval, #autosync').on('change keyup', function(){
startAutosync();
});
}); });
</script> </script>
</body> </body>
</html> </html>
<?php
exit;
} else {
// Dashboard-Übersicht: Wenn kein action=setup übergeben wird, wird die aggregierte Ansicht angezeigt.
?>
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<title>Dashboard Übersicht</title>
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<style>
.tile { border: 1px solid #ccc; padding: 10px; margin: 10px; display: inline-block; vertical-align: top; width: 200px; }
.bar-container { height: 100px; width: 30px; display: inline-block; margin-right: 5px; background: #eee; position: relative; }
.bar { position: absolute; bottom: 0; width: 100%; background: #3b82f6; }
.label { text-align: center; font-size: 12px; }
</style>
</head>
<body>
<h1>Dashboard Übersicht</h1>
<div id="tiles-container">
<!-- Hier werden die Kacheln angezeigt -->
</div>
<script>
function loadDashboard(){
$.ajax({
url: 'dashboard.php',
type: 'POST',
dataType: 'json',
data: { action: 'get_dashboard_data' },
success: function(resp){
var data = resp.data;
var maxReplies = resp.max.replies;
var maxReblogs = resp.max.reblogs;
var maxFavorites = resp.max.favorites;
var container = $('#tiles-container');
container.empty();
// Sortierung nach Hashtag und dann Username
data.sort(function(a, b){
if(a.hashtag === b.hashtag) {
return a.username.localeCompare(b.username);
}
return a.hashtag.localeCompare(b.hashtag);
});
$.each(data, function(i, row){
var repliesHeight = maxReplies > 0 ? (row.total_replies / maxReplies) * 100 : 0;
var reblogsHeight = maxReblogs > 0 ? (row.total_reblogs / maxReblogs) * 100 : 0;
var favoritesHeight = maxFavorites > 0 ? (row.total_favorites / maxFavorites) * 100 : 0;
var tile = $('<div class="tile"></div>');
tile.append('<h3>' + row.hashtag + '</h3>');
tile.append('<p>' + row.username + '</p>');
var bars = $('<div></div>');
bars.append('<div class="bar-container"><div class="bar" style="height:'+repliesHeight+'%"></div><div class="label">Replies ('+row.total_replies+')</div></div>');
bars.append('<div class="bar-container"><div class="bar" style="height:'+reblogsHeight+'%"></div><div class="label">Reblogs ('+row.total_reblogs+')</div></div>');
bars.append('<div class="bar-container"><div class="bar" style="height:'+favoritesHeight+'%"></div><div class="label">Likes ('+row.total_favorites+')</div></div>');
tile.append(bars);
container.append(tile);
});
}
});
}
// Initiales Laden
loadDashboard();
// Aktualisierung im Hintergrund Intervall aus der Config (Standard 20 Sek.)
setInterval(loadDashboard, 20000);
</script>
</body>
</html>
<?php
}
?>