Statistik backend and favicon
This commit is contained in:
@@ -4,6 +4,7 @@
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Admin Dashboard - NinjaCross</title>
|
||||
<link rel="icon" type="image/x-icon" href="/pictures/favicon.ico">
|
||||
<link rel="stylesheet" href="/css/admin-dashboard.css">
|
||||
</head>
|
||||
<body>
|
||||
@@ -40,6 +41,48 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Hauptseiten-Besuche -->
|
||||
<div class="page-stats-section">
|
||||
<h2>🏠 Hauptseiten-Besuche</h2>
|
||||
<div class="page-stats-grid">
|
||||
<div class="page-stat-card">
|
||||
<h3>Heute</h3>
|
||||
<div id="todayStats" class="page-stats-content">Lade...</div>
|
||||
</div>
|
||||
<div class="page-stat-card">
|
||||
<h3>Diese Woche</h3>
|
||||
<div id="weekStats" class="page-stats-content">Lade...</div>
|
||||
</div>
|
||||
<div class="page-stat-card">
|
||||
<h3>Dieser Monat</h3>
|
||||
<div id="monthStats" class="page-stats-content">Lade...</div>
|
||||
</div>
|
||||
<div class="page-stat-card">
|
||||
<h3>Gesamt</h3>
|
||||
<div id="totalStats" class="page-stats-content">Lade...</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Verlinkungs-Statistiken -->
|
||||
<div class="link-stats-section">
|
||||
<h3>🔗 Account-Verknüpfungen</h3>
|
||||
<div class="link-stats-grid">
|
||||
<div class="link-stat-card">
|
||||
<div class="link-stat-number" id="totalPlayersCount">-</div>
|
||||
<div class="link-stat-label">Gesamt Spieler</div>
|
||||
</div>
|
||||
<div class="link-stat-card">
|
||||
<div class="link-stat-number" id="linkedPlayersCount">-</div>
|
||||
<div class="link-stat-label">Verknüpfte Spieler</div>
|
||||
</div>
|
||||
<div class="link-stat-card">
|
||||
<div class="link-stat-number" id="linkPercentage">-</div>
|
||||
<div class="link-stat-label">Verknüpfungsrate</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Dashboard Cards -->
|
||||
<div class="dashboard-grid">
|
||||
<!-- Benutzer-Verwaltung -->
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Login - Lizenzgenerator</title>
|
||||
<link rel="icon" type="image/x-icon" href="/pictures/favicon.ico">
|
||||
<link rel="stylesheet" href="/css/adminlogin.css">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
@@ -326,6 +326,121 @@ body {
|
||||
margin: 5% auto;
|
||||
width: 95%;
|
||||
}
|
||||
|
||||
.page-stats-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.link-stats-grid {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
}
|
||||
}
|
||||
|
||||
/* Page Statistics Styles */
|
||||
.page-stats-section {
|
||||
background: white;
|
||||
border-radius: 10px;
|
||||
padding: 25px;
|
||||
margin: 20px 0;
|
||||
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.page-stats-section h2 {
|
||||
color: #1e3c72;
|
||||
margin-bottom: 20px;
|
||||
font-size: 1.5em;
|
||||
}
|
||||
|
||||
.page-stats-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
||||
gap: 20px;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.page-stat-card {
|
||||
background: #f8f9fa;
|
||||
border-radius: 8px;
|
||||
padding: 20px;
|
||||
border-left: 4px solid #1e3c72;
|
||||
}
|
||||
|
||||
.page-stat-card h3 {
|
||||
color: #1e3c72;
|
||||
margin-bottom: 15px;
|
||||
font-size: 1.2em;
|
||||
}
|
||||
|
||||
.page-stats-content {
|
||||
font-size: 0.9em;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.page-stats-content .page-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 8px 0;
|
||||
border-bottom: 1px solid #e9ecef;
|
||||
}
|
||||
|
||||
.page-stats-content .page-item:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.page-stats-content .page-name {
|
||||
font-weight: 500;
|
||||
color: #495057;
|
||||
}
|
||||
|
||||
.page-stats-content .page-count {
|
||||
background: #1e3c72;
|
||||
color: white;
|
||||
padding: 4px 8px;
|
||||
border-radius: 12px;
|
||||
font-size: 0.8em;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* Link Statistics Styles */
|
||||
.link-stats-section {
|
||||
background: #f8f9fa;
|
||||
border-radius: 8px;
|
||||
padding: 20px;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.link-stats-section h3 {
|
||||
color: #1e3c72;
|
||||
margin-bottom: 15px;
|
||||
font-size: 1.3em;
|
||||
}
|
||||
|
||||
.link-stats-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.link-stat-card {
|
||||
text-align: center;
|
||||
background: white;
|
||||
border-radius: 8px;
|
||||
padding: 20px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.link-stat-number {
|
||||
font-size: 2em;
|
||||
font-weight: bold;
|
||||
color: #1e3c72;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.link-stat-label {
|
||||
color: #6c757d;
|
||||
font-size: 0.9em;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>SPEEDRUN ARENA - Admin Dashboard</title>
|
||||
<link rel="icon" type="image/x-icon" href="/pictures/favicon.ico">
|
||||
<script src="https://unpkg.com/@supabase/supabase-js@2"></script>
|
||||
<!-- QR Code Scanner Library -->
|
||||
<script src="https://unpkg.com/jsqr@1.4.0/dist/jsQR.js"></script>
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Lizenzgenerator</title>
|
||||
<link rel="icon" type="image/x-icon" href="/pictures/favicon.ico">
|
||||
<link rel="stylesheet" href="/css/generator.css">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Timer Leaderboard</title>
|
||||
<link rel="icon" type="image/x-icon" href="/pictures/favicon.ico">
|
||||
<script src="/js/page-tracking.js"></script>
|
||||
<link rel="stylesheet" href="/css/leaderboard.css">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
@@ -6,6 +6,7 @@ let currentData = [];
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
checkAuth();
|
||||
loadStatistics();
|
||||
loadPageStatistics();
|
||||
setupEventListeners();
|
||||
});
|
||||
|
||||
@@ -87,6 +88,66 @@ async function loadStatistics() {
|
||||
}
|
||||
}
|
||||
|
||||
async function loadPageStatistics() {
|
||||
try {
|
||||
const response = await fetch('/api/admin-page-stats');
|
||||
const result = await response.json();
|
||||
|
||||
if (result.success) {
|
||||
const data = result.data;
|
||||
|
||||
// Display page view statistics
|
||||
displayPageStats('todayStats', data.today);
|
||||
displayPageStats('weekStats', data.week);
|
||||
displayPageStats('monthStats', data.month);
|
||||
displayPageStats('totalStats', data.total);
|
||||
|
||||
// Display link statistics
|
||||
if (data.linkStats) {
|
||||
document.getElementById('totalPlayersCount').textContent = data.linkStats.total_players || 0;
|
||||
document.getElementById('linkedPlayersCount').textContent = data.linkStats.linked_players || 0;
|
||||
document.getElementById('linkPercentage').textContent = `${data.linkStats.link_percentage || 0}%`;
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to load page statistics:', error);
|
||||
// Show error in all stat containers
|
||||
['todayStats', 'weekStats', 'monthStats', 'totalStats'].forEach(id => {
|
||||
document.getElementById(id).innerHTML = '<div style="color: #dc3545;">Fehler beim Laden</div>';
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function displayPageStats(containerId, stats) {
|
||||
const container = document.getElementById(containerId);
|
||||
|
||||
if (!stats || stats.length === 0) {
|
||||
container.innerHTML = '<div style="color: #6c757d;">0</div>';
|
||||
return;
|
||||
}
|
||||
|
||||
// Find main page visits
|
||||
const mainPageStat = stats.find(stat => stat.page === 'main_page_visit');
|
||||
const count = mainPageStat ? mainPageStat.count : 0;
|
||||
|
||||
container.innerHTML = `<div style="font-size: 1.5em; font-weight: bold; color: #1e3c72;">${count}</div>`;
|
||||
}
|
||||
|
||||
function getPageDisplayName(page) {
|
||||
const pageNames = {
|
||||
'main_page_visit': '🏠 Hauptseiten-Besuche',
|
||||
'home': '🏠 Hauptseite',
|
||||
'login': '🔐 Login',
|
||||
'dashboard': '📊 Dashboard',
|
||||
'admin_login': '🛡️ Admin Login',
|
||||
'admin_dashboard': '🛡️ Admin Dashboard',
|
||||
'license_generator': '🔧 Lizenzgenerator',
|
||||
'reset_password': '🔑 Passwort Reset'
|
||||
};
|
||||
|
||||
return pageNames[page] || page;
|
||||
}
|
||||
|
||||
function showUserManagement() {
|
||||
showDataSection('users', 'Benutzer-Verwaltung');
|
||||
loadUsers();
|
||||
|
||||
33
public/js/page-tracking.js
Normal file
33
public/js/page-tracking.js
Normal file
@@ -0,0 +1,33 @@
|
||||
// Page tracking functionality
|
||||
function trackPageView(pageName) {
|
||||
// Get user information
|
||||
const userAgent = navigator.userAgent;
|
||||
const referer = document.referrer || '';
|
||||
|
||||
// Send tracking data to server
|
||||
fetch('/api/track-page-view', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
page: pageName,
|
||||
userAgent: userAgent,
|
||||
ipAddress: null, // Will be determined by server
|
||||
referer: referer
|
||||
})
|
||||
}).catch(error => {
|
||||
console.log('Page tracking failed:', error);
|
||||
// Silently fail - don't interrupt user experience
|
||||
});
|
||||
}
|
||||
|
||||
// Auto-track page on load - only track main page visits
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// Only track the main page (index.html or root path)
|
||||
const path = window.location.pathname;
|
||||
|
||||
if (path === '/' || path === '/index.html') {
|
||||
trackPageView('main_page_visit');
|
||||
}
|
||||
});
|
||||
@@ -4,6 +4,7 @@
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Ninja Server - Admin Login</title>
|
||||
<link rel="icon" type="image/x-icon" href="/pictures/favicon.ico">
|
||||
<script src="https://unpkg.com/@supabase/supabase-js@2"></script>
|
||||
<link rel="stylesheet" href="/css/login.css">
|
||||
</head>
|
||||
|
||||
BIN
public/pictures/favicon.ico
Normal file
BIN
public/pictures/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
@@ -4,6 +4,7 @@
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Passwort zurücksetzen - NinjaCross</title>
|
||||
<link rel="icon" type="image/x-icon" href="/pictures/favicon.ico">
|
||||
|
||||
<!-- Supabase -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/@supabase/supabase-js@2"></script>
|
||||
|
||||
102
routes/api.js
102
routes/api.js
@@ -1622,6 +1622,108 @@ router.delete('/admin-adminusers/:id', requireAdminAuth, async (req, res) => {
|
||||
}
|
||||
});
|
||||
|
||||
// ============================================================================
|
||||
// PAGE VIEWS TRACKING
|
||||
// ============================================================================
|
||||
|
||||
// Track page view
|
||||
router.post('/track-page-view', async (req, res) => {
|
||||
try {
|
||||
const { page, userAgent, ipAddress, referer } = req.body;
|
||||
|
||||
await pool.query(`
|
||||
INSERT INTO page_views (page, user_agent, ip_address, referer)
|
||||
VALUES ($1, $2, $3, $4)
|
||||
`, [page, userAgent, ipAddress, referer]);
|
||||
|
||||
res.json({ success: true });
|
||||
} catch (error) {
|
||||
console.error('Error tracking page view:', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: 'Fehler beim Tracking der Seitenaufrufe'
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Get page statistics
|
||||
router.get('/admin-page-stats', requireAdminAuth, async (req, res) => {
|
||||
try {
|
||||
// Page views for today, this week, this month
|
||||
const today = new Date();
|
||||
const startOfDay = new Date(today.getFullYear(), today.getMonth(), today.getDate());
|
||||
const startOfWeek = new Date(today);
|
||||
startOfWeek.setDate(today.getDate() - today.getDay());
|
||||
startOfWeek.setHours(0, 0, 0, 0);
|
||||
const startOfMonth = new Date(today.getFullYear(), today.getMonth(), 1);
|
||||
|
||||
// Today's page views
|
||||
const todayViews = await pool.query(`
|
||||
SELECT page, COUNT(*) as count
|
||||
FROM page_views
|
||||
WHERE created_at >= $1
|
||||
GROUP BY page
|
||||
ORDER BY count DESC
|
||||
`, [startOfDay]);
|
||||
|
||||
// This week's page views
|
||||
const weekViews = await pool.query(`
|
||||
SELECT page, COUNT(*) as count
|
||||
FROM page_views
|
||||
WHERE created_at >= $1
|
||||
GROUP BY page
|
||||
ORDER BY count DESC
|
||||
`, [startOfWeek]);
|
||||
|
||||
// This month's page views
|
||||
const monthViews = await pool.query(`
|
||||
SELECT page, COUNT(*) as count
|
||||
FROM page_views
|
||||
WHERE created_at >= $1
|
||||
GROUP BY page
|
||||
ORDER BY count DESC
|
||||
`, [startOfMonth]);
|
||||
|
||||
// Total page views
|
||||
const totalViews = await pool.query(`
|
||||
SELECT page, COUNT(*) as count
|
||||
FROM page_views
|
||||
GROUP BY page
|
||||
ORDER BY count DESC
|
||||
`);
|
||||
|
||||
// Player/Supabase link statistics
|
||||
const linkStats = await pool.query(`
|
||||
SELECT
|
||||
COUNT(*) as total_players,
|
||||
COUNT(CASE WHEN supabase_user_id IS NOT NULL THEN 1 END) as linked_players,
|
||||
CAST(
|
||||
ROUND(
|
||||
(COUNT(CASE WHEN supabase_user_id IS NOT NULL THEN 1 END)::numeric / COUNT(*)) * 100, 2
|
||||
) AS DECIMAL(5,2)
|
||||
) as link_percentage
|
||||
FROM players
|
||||
`);
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: {
|
||||
today: todayViews.rows,
|
||||
week: weekViews.rows,
|
||||
month: monthViews.rows,
|
||||
total: totalViews.rows,
|
||||
linkStats: linkStats.rows[0]
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Error loading page statistics:', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: 'Fehler beim Laden der Seitenstatistiken'
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// ============================================================================
|
||||
// POST/PUT ROUTES FÜR CRUD-OPERATIONEN
|
||||
// ============================================================================
|
||||
|
||||
Reference in New Issue
Block a user