396 lines
15 KiB
PHP
396 lines
15 KiB
PHP
<?php
|
||
error_reporting(E_ALL);
|
||
ini_set('display_errors', 1);
|
||
|
||
// dashboard.php
|
||
|
||
// Zentrale Zugangsdaten einbinden
|
||
require_once 'secrets.php';
|
||
|
||
// Datenbank-Verbindungsparameter
|
||
$dsn = "mysql:host=" . DB_HOST . ";dbname=" . DB_NAME . ";charset=" . DB_CHARSET;
|
||
$options = [
|
||
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
|
||
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
|
||
];
|
||
|
||
try {
|
||
$pdo = new PDO($dsn, DB_USER, DB_PASS, $options);
|
||
} catch (PDOException $e) {
|
||
header('Content-Type: application/json');
|
||
echo json_encode(['error' => 'Database connection failed: ' . $e->getMessage()]);
|
||
exit;
|
||
}
|
||
|
||
// Bearer Token für Mastodon-API (falls benötigt)
|
||
$bearerToken = 'your_bearer_token_here';
|
||
|
||
// -----------------------
|
||
// AJAX-Anfragen (POST)
|
||
// -----------------------
|
||
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action'])) {
|
||
$action = $_POST['action'];
|
||
header('Content-Type: application/json');
|
||
|
||
if ($action === 'save_config') {
|
||
// 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'] : [];
|
||
$response = ['saved' => 0, 'updated' => 0];
|
||
foreach ($hashtags as $row) {
|
||
if (isset($row['hashtag_id']) && !empty($row['hashtag_id'])) {
|
||
// Update
|
||
$stmt = $pdo->prepare("UPDATE hashtags SET hashtag = :hashtag, active = :active WHERE hashtag_id = :hashtag_id");
|
||
$stmt->execute([
|
||
'hashtag' => $row['hashtag'],
|
||
'active' => !empty($row['active']) ? 1 : 0,
|
||
'hashtag_id' => $row['hashtag_id']
|
||
]);
|
||
$response['updated']++;
|
||
} else {
|
||
// Insert neuer Eintrag
|
||
$stmt = $pdo->prepare("INSERT INTO hashtags (hashtag, active) VALUES (:hashtag, :active)");
|
||
$stmt->execute([
|
||
'hashtag' => $row['hashtag'],
|
||
'active' => !empty($row['active']) ? 1 : 0
|
||
]);
|
||
$response['saved']++;
|
||
}
|
||
}
|
||
echo json_encode($response);
|
||
exit;
|
||
} elseif ($action === 'save_accounts') {
|
||
// Erwartet ein Array "accounts" (Felder: acc_id, active)
|
||
$accounts = isset($_POST['accounts']) ? $_POST['accounts'] : [];
|
||
$response = ['updated' => 0];
|
||
foreach ($accounts as $row) {
|
||
// Anpassen: WHERE user_id = :acc_id
|
||
$stmt = $pdo->prepare("UPDATE users SET active = :active WHERE user_id = :acc_id");
|
||
$stmt->execute([
|
||
'active' => !empty($row['active']) ? 1 : 0,
|
||
'acc_id' => $row['acc_id']
|
||
]);
|
||
$response['updated']++;
|
||
}
|
||
echo json_encode($response);
|
||
exit;
|
||
} elseif ($action === 'sync_accounts') {
|
||
// Synchronisation: Aus der findings-Tabelle werden alle getrackten Accounts (acc_id, username) ermittelt
|
||
// Für jeden Account, der noch nicht in der "users"-Tabelle existiert, erfolgt ein Insert mit active=0
|
||
$stmt = $pdo->query("SELECT DISTINCT f.acc_id, f.username FROM findings f LEFT JOIN users u ON f.acc_id = u.user_id WHERE u.user_id IS NULL");
|
||
$newAccounts = $stmt->fetchAll();
|
||
|
||
foreach ($newAccounts as $acc) {
|
||
// Neuer Account – unaktiv in die Tabelle schreiben
|
||
$insertStmt = $pdo->prepare("INSERT INTO users (user_id, username, active) VALUES (:acc_id, :username, 0)");
|
||
$insertStmt->execute([
|
||
'acc_id' => $acc['acc_id'],
|
||
'username' => $acc['username']
|
||
]);
|
||
}
|
||
// Nun alle Accounts aus der users-Tabelle abrufen
|
||
$stmt = $pdo->query("SELECT user_id as acc_id, username, active FROM users ORDER BY username ASC");
|
||
$accountsList = $stmt->fetchAll();
|
||
echo json_encode($accountsList);
|
||
exit;
|
||
} elseif ($action === 'get_dashboard_data') {
|
||
// Lese den Zeitraum und Refresh-Intervall aus der config-Tabelle (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 H:i:s', strtotime('-1 day')), 'to_date' => date('Y-m-d H:i:s'), 'refresh_interval' => 20];
|
||
}
|
||
// Aggregation: Nur Beiträge innerhalb des Zeitraums, zu aktiven Hashtags und von aktiven Usern
|
||
$query = "SELECT f.username, h.hashtag,
|
||
SUM(f.replies_count) AS total_replies,
|
||
SUM(f.reblogs_count) AS total_reblogs,
|
||
SUM(f.favorites_count) AS total_favorites
|
||
FROM findings f
|
||
JOIN hashtags h ON f.hashtag_id = h.hashtag_id
|
||
JOIN users u ON f.acc_id = u.user_id
|
||
WHERE f.created_at BETWEEN :from_date AND :to_date
|
||
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([
|
||
'data' => $data,
|
||
'max' => ['replies' => $maxReplies, 'reblogs' => $maxReblogs, 'favorites' => $maxFavorites]
|
||
]);
|
||
exit;
|
||
}
|
||
exit;
|
||
}
|
||
|
||
// -----------------------
|
||
// 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, verknüpft mit der users-Tabelle
|
||
$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.user_id
|
||
ORDER BY f.username ASC");
|
||
$users = $userStmt->fetchAll();
|
||
?>
|
||
<!DOCTYPE html>
|
||
<html lang="de">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<title>Dashboard Konfiguration</title>
|
||
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
|
||
<style>
|
||
table { border-collapse: collapse; width: 100%; margin-bottom: 1em; }
|
||
th, td { border: 1px solid #ccc; padding: 5px; text-align: left; }
|
||
.error { color: red; }
|
||
.config-section { margin-bottom: 2em; padding: 1em; border: 1px solid #ddd; }
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<h1>Dashboard Konfiguration</h1>
|
||
|
||
<!-- Zeitraum-Konfiguration -->
|
||
<div class="config-section">
|
||
<h2>Zeitraum</h2>
|
||
<form id="config-form">
|
||
<label for="from_date">Von:</label>
|
||
<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>
|
||
|
||
<!-- Hashtag-Konfiguration -->
|
||
<div class="config-section">
|
||
<h2>Hashtags</h2>
|
||
<table id="hashtag-config-table">
|
||
<thead>
|
||
<tr>
|
||
<th>Hashtag</th>
|
||
<th>Aktiv</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<?php foreach($hashtags as $h): ?>
|
||
<tr data-id="<?php echo $h['hashtag_id']; ?>">
|
||
<td><input type="text" class="hashtag-text" value="<?php echo htmlspecialchars($h['hashtag']); ?>"></td>
|
||
<td><input type="checkbox" class="hashtag-active" <?php echo ($h['active'] == 1) ? 'checked' : ''; ?>></td>
|
||
</tr>
|
||
<?php endforeach; ?>
|
||
</tbody>
|
||
</table>
|
||
<button id="add-hashtag">+1</button>
|
||
<button id="save-hashtags">Speichern</button>
|
||
</div>
|
||
|
||
<!-- User-Konfiguration -->
|
||
<div class="config-section">
|
||
<h2>Getrackte User</h2>
|
||
<table id="user-config-table">
|
||
<thead>
|
||
<tr>
|
||
<th>Acc_ID</th>
|
||
<th>Username</th>
|
||
<th>Aktiv</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<?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>
|
||
</table>
|
||
<button id="save-users">Speichern</button>
|
||
</div>
|
||
|
||
<script>
|
||
$(document).ready(function(){
|
||
// Konfigurationsspeicherung für Zeitraum
|
||
$('#save-config').click(function(){
|
||
$.ajax({
|
||
url: 'dashboard.php',
|
||
type: 'POST',
|
||
dataType: 'json',
|
||
data: {
|
||
action: 'save_config',
|
||
from_date: $('#from_date').val(),
|
||
to_date: $('#to_date').val(),
|
||
refresh_interval: $('#refresh_interval').val()
|
||
},
|
||
success: function(resp) {
|
||
alert('Konfiguration gespeichert.');
|
||
}
|
||
});
|
||
});
|
||
|
||
// Hashtag-Konfiguration speichern
|
||
$('#save-hashtags').click(function(){
|
||
var hashtags = [];
|
||
$('#hashtag-config-table tbody tr').each(function(){
|
||
var id = $(this).data('id');
|
||
var text = $(this).find('.hashtag-text').val().trim();
|
||
var active = $(this).find('.hashtag-active').is(':checked') ? 1 : 0;
|
||
if(text !== ""){
|
||
hashtags.push({hashtag_id: id, hashtag: text, active: active});
|
||
}
|
||
});
|
||
$.ajax({
|
||
url: 'dashboard.php',
|
||
type: 'POST',
|
||
dataType: 'json',
|
||
data: { action: 'save_hashtags', hashtags: hashtags },
|
||
success: function(resp){
|
||
alert('Hashtags gespeichert.');
|
||
location.reload();
|
||
}
|
||
});
|
||
});
|
||
|
||
// User-Konfiguration speichern
|
||
$('#save-users').click(function(){
|
||
var accounts = [];
|
||
$('#user-config-table tbody tr').each(function(){
|
||
var acc_id = $(this).data('id');
|
||
var active = $(this).find('.user-active').is(':checked') ? 1 : 0;
|
||
accounts.push({acc_id: acc_id, active: active});
|
||
});
|
||
$.ajax({
|
||
url: 'dashboard.php',
|
||
type: 'POST',
|
||
dataType: 'json',
|
||
data: { action: 'save_accounts', accounts: accounts },
|
||
success: function(resp){
|
||
alert('User gespeichert.');
|
||
}
|
||
});
|
||
});
|
||
});
|
||
</script>
|
||
</body>
|
||
</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
|
||
}
|
||
?>
|