mirror of
https://github.com/Dvorinka/PPve.git
synced 2026-06-05 13:02:58 +00:00
fes
This commit is contained in:
+28
-9
@@ -96,17 +96,36 @@ async function checkAchievements() {
|
||||
try {
|
||||
const response = await fetch('/api/visitor-stats');
|
||||
const stats = await response.json();
|
||||
|
||||
// Check for monthly achievements
|
||||
Object.values(ACHIEVEMENTS).forEach(achievement => {
|
||||
if (achievement.period === "monthly" && stats.monthly_visits >= achievement.threshold) {
|
||||
unlockAchievement(achievement);
|
||||
}
|
||||
});
|
||||
const visitorId = getCookie('visitor_id');
|
||||
|
||||
// First visit achievement
|
||||
if (stats.total_visits === 1) {
|
||||
unlockAchievement(ACHIEVEMENTS.first_visit);
|
||||
if (!localStorage.getItem('first_visit_' + visitorId)) {
|
||||
unlockAchievement('first_visit');
|
||||
localStorage.setItem('first_visit_' + visitorId, 'true');
|
||||
}
|
||||
|
||||
// Get visitor's stats
|
||||
const visitor = stats.unique_visitors[visitorId];
|
||||
if (!visitor) return;
|
||||
|
||||
// Monthly achievements
|
||||
const monthlyVisits = stats.monthly_visits;
|
||||
if (monthlyVisits >= ACHIEVEMENTS.frequent_visitor.threshold &&
|
||||
!localStorage.getItem('frequent_visitor_' + visitorId)) {
|
||||
unlockAchievement('frequent_visitor');
|
||||
localStorage.setItem('frequent_visitor_' + visitorId, 'true');
|
||||
}
|
||||
|
||||
if (monthlyVisits >= ACHIEVEMENTS.power_user.threshold &&
|
||||
!localStorage.getItem('power_user_' + visitorId)) {
|
||||
unlockAchievement('power_user');
|
||||
localStorage.setItem('power_user_' + visitorId, 'true');
|
||||
}
|
||||
|
||||
if (monthlyVisits >= ACHIEVEMENTS.super_fan.threshold &&
|
||||
!localStorage.getItem('super_fan_' + visitorId)) {
|
||||
unlockAchievement('super_fan');
|
||||
localStorage.setItem('super_fan_' + visitorId, 'true');
|
||||
}
|
||||
|
||||
// Apply highest unlocked achievement theme
|
||||
|
||||
+66
-45
@@ -4367,36 +4367,48 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
|
||||
// Browser stats
|
||||
const browserStats = document.getElementById('browserStats');
|
||||
browserStats.innerHTML = Object.entries(stats.browser_stats)
|
||||
.sort((a, b) => b[1] - a[1])
|
||||
.map(([browser, count]) => `
|
||||
<div class="flex justify-between items-center">
|
||||
<span class="text-sm">${browser}</span>
|
||||
<span class="text-sm text-gray-600">${count}</span>
|
||||
</div>
|
||||
`).join('');
|
||||
if (stats.browser_stats) {
|
||||
browserStats.innerHTML = Object.entries(stats.browser_stats)
|
||||
.sort((a, b) => b[1] - a[1])
|
||||
.map(([browser, count]) => `
|
||||
<div class="flex justify-between items-center">
|
||||
<span class="text-sm">${browser}</span>
|
||||
<span class="text-sm text-gray-600">${count}</span>
|
||||
</div>
|
||||
`).join('');
|
||||
} else {
|
||||
browserStats.innerHTML = '<div class="text-sm text-gray-500">Žádná data</div>';
|
||||
}
|
||||
|
||||
// OS stats
|
||||
const osStats = document.getElementById('osStats');
|
||||
osStats.innerHTML = Object.entries(stats.os_stats)
|
||||
.sort((a, b) => b[1] - a[1])
|
||||
.map(([os, count]) => `
|
||||
<div class="flex justify-between items-center">
|
||||
<span class="text-sm">${os}</span>
|
||||
<span class="text-sm text-gray-600">${count}</span>
|
||||
</div>
|
||||
`).join('');
|
||||
if (stats.os_stats) {
|
||||
osStats.innerHTML = Object.entries(stats.os_stats)
|
||||
.sort((a, b) => b[1] - a[1])
|
||||
.map(([os, count]) => `
|
||||
<div class="flex justify-between items-center">
|
||||
<span class="text-sm">${os}</span>
|
||||
<span class="text-sm text-gray-600">${count}</span>
|
||||
</div>
|
||||
`).join('');
|
||||
} else {
|
||||
osStats.innerHTML = '<div class="text-sm text-gray-500">Žádná data</div>';
|
||||
}
|
||||
|
||||
// Active hours
|
||||
const activeHours = document.getElementById('activeHours');
|
||||
activeHours.innerHTML = stats.most_active_hours
|
||||
.sort((a, b) => b.count - a.count)
|
||||
.map(hour => `
|
||||
<div class="flex justify-between items-center">
|
||||
<span class="text-sm">${hour.hour}:00</span>
|
||||
<span class="text-sm text-gray-600">${hour.count}</span>
|
||||
</div>
|
||||
`).join('');
|
||||
if (stats.most_active_hours && stats.most_active_hours.length > 0) {
|
||||
activeHours.innerHTML = stats.most_active_hours
|
||||
.sort((a, b) => b.count - a.count)
|
||||
.map(hour => `
|
||||
<div class="flex justify-between items-center">
|
||||
<span class="text-sm">${hour.hour}:00</span>
|
||||
<span class="text-sm text-gray-600">${hour.count}</span>
|
||||
</div>
|
||||
`).join('');
|
||||
} else {
|
||||
activeHours.innerHTML = '<div class="text-sm text-gray-500">Žádná data</div>';
|
||||
}
|
||||
|
||||
// Active days
|
||||
const activeDays = document.getElementById('activeDays');
|
||||
@@ -4410,31 +4422,40 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
'Sunday': 'Neděle'
|
||||
};
|
||||
|
||||
activeDays.innerHTML = stats.most_active_days
|
||||
.sort((a, b) => b.count - a.count)
|
||||
.map(day => `
|
||||
<div class="flex justify-between items-center">
|
||||
<span class="text-sm">${dayMap[day.day] || day.day}</span>
|
||||
<span class="text-sm text-gray-600">${day.count}</span>
|
||||
</div>
|
||||
`).join('');
|
||||
if (stats.most_active_days && stats.most_active_days.length > 0) {
|
||||
activeDays.innerHTML = stats.most_active_days
|
||||
.sort((a, b) => b.count - a.count)
|
||||
.filter(day => day.count > 0) // Filter out days with 0 visits
|
||||
.map(day => `
|
||||
<div class="flex justify-between items-center">
|
||||
<span class="text-sm">${dayMap[day.day] || day.day}</span>
|
||||
<span class="text-sm text-gray-600">${day.count}</span>
|
||||
</div>
|
||||
`).join('');
|
||||
} else {
|
||||
activeDays.innerHTML = '<div class="text-sm text-gray-500">Žádná data</div>';
|
||||
}
|
||||
|
||||
// Unique visitors
|
||||
const uniqueVisitors = document.getElementById('uniqueVisitors');
|
||||
uniqueVisitors.innerHTML = Object.entries(stats.unique_visitors)
|
||||
.map(([id, visitor]) => `
|
||||
<div class="flex justify-between items-center p-2 hover:bg-gray-50 rounded">
|
||||
<div class="flex-1">
|
||||
<div class="text-sm">${visitor.ip}</div>
|
||||
<div class="text-xs text-gray-500">${visitor.user_agent}</div>
|
||||
if (stats.unique_visitors) {
|
||||
uniqueVisitors.innerHTML = Object.entries(stats.unique_visitors)
|
||||
.map(([id, visitor]) => `
|
||||
<div class="flex justify-between items-center p-2 hover:bg-gray-50 rounded">
|
||||
<div class="flex-1">
|
||||
<div class="text-sm">${visitor.ip}</div>
|
||||
<div class="text-xs text-gray-500">${visitor.user_agent}</div>
|
||||
</div>
|
||||
<div class="flex items-center space-x-2">
|
||||
<span class="text-xs text-gray-600">${visitor.visits} návštěv</span>
|
||||
<span class="text-xs text-gray-400">|</span>
|
||||
<span class="text-xs text-gray-600">${visitor.last_visit.toLocaleString()}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center space-x-2">
|
||||
<span class="text-xs text-gray-600">${visitor.visits} návštěv</span>
|
||||
<span class="text-xs text-gray-400">|</span>
|
||||
<span class="text-xs text-gray-600">${visitor.last_visit.toLocaleString()}</span>
|
||||
</div>
|
||||
</div>
|
||||
`).join('');
|
||||
`).join('');
|
||||
} else {
|
||||
uniqueVisitors.innerHTML = '<div class="text-sm text-gray-500">Žádná data</div>';
|
||||
}
|
||||
|
||||
initializeCharts();
|
||||
updateVisitorStats(stats);
|
||||
|
||||
+29
-21
@@ -618,33 +618,41 @@
|
||||
});
|
||||
</script>
|
||||
<!-- Achievements display -->
|
||||
<div id="achievementsDisplay">
|
||||
<div class="achievement-item">
|
||||
<i class="fas fa-star achievement-icon text-yellow-500"></i>
|
||||
<div>
|
||||
<h4 class="font-bold">Nováček</h4>
|
||||
<p class="text-sm text-gray-600">První návštěva na portálu</p>
|
||||
<div id="achievementsDisplay" class="hidden">
|
||||
<div class="achievement-item bg-white rounded-lg shadow-md p-4 mb-4">
|
||||
<div class="flex items-center space-x-4">
|
||||
<i class="fas fa-star achievement-icon text-yellow-500"></i>
|
||||
<div>
|
||||
<h4 class="font-bold">Nováček</h4>
|
||||
<p class="text-sm text-gray-600">První návštěva na portálu</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="achievement-item">
|
||||
<i class="fas fa-clock-rotate-left achievement-icon text-blue-500"></i>
|
||||
<div>
|
||||
<h4 class="font-bold">Pravidelný návštěvník</h4>
|
||||
<p class="text-sm text-gray-600">10 návštěv za měsíc</p>
|
||||
<div class="achievement-item bg-white rounded-lg shadow-md p-4 mb-4">
|
||||
<div class="flex items-center space-x-4">
|
||||
<i class="fas fa-clock-rotate-left achievement-icon text-blue-500"></i>
|
||||
<div>
|
||||
<h4 class="font-bold">Pravidelný návštěvník</h4>
|
||||
<p class="text-sm text-gray-600">10 návštěv za měsíc</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="achievement-item">
|
||||
<i class="fas fa-rocket achievement-icon text-purple-500"></i>
|
||||
<div>
|
||||
<h4 class="font-bold">Power User</h4>
|
||||
<p class="text-sm text-gray-600">50 návštěv za měsíc</p>
|
||||
<div class="achievement-item bg-white rounded-lg shadow-md p-4 mb-4">
|
||||
<div class="flex items-center space-x-4">
|
||||
<i class="fas fa-rocket achievement-icon text-purple-500"></i>
|
||||
<div>
|
||||
<h4 class="font-bold">Power User</h4>
|
||||
<p class="text-sm text-gray-600">50 návštěv za měsíc</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="achievement-item">
|
||||
<i class="fas fa-award achievement-icon text-gold"></i>
|
||||
<div>
|
||||
<h4 class="font-bold">Super Fan</h4>
|
||||
<p class="text-sm text-gray-600">100 návštěv za měsíc</p>
|
||||
<div class="achievement-item bg-white rounded-lg shadow-md p-4 mb-4">
|
||||
<div class="flex items-center space-x-4">
|
||||
<i class="fas fa-award achievement-icon text-gold"></i>
|
||||
<div>
|
||||
<h4 class="font-bold">Super Fan</h4>
|
||||
<p class="text-sm text-gray-600">100 návštěv za měsíc</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -199,6 +199,63 @@ func trackVisit(w http.ResponseWriter, r *http.Request) {
|
||||
stats.UniqueVisitors[visitorId] = visitor
|
||||
}
|
||||
|
||||
// Update browser stats
|
||||
browser := detectBrowser(userAgent)
|
||||
if stats.BrowserStats == nil {
|
||||
stats.BrowserStats = make(map[string]int)
|
||||
}
|
||||
stats.BrowserStats[browser]++
|
||||
|
||||
// Update OS stats
|
||||
os := detectOS(userAgent)
|
||||
if stats.OSStats == nil {
|
||||
stats.OSStats = make(map[string]int)
|
||||
}
|
||||
stats.OSStats[os]++
|
||||
|
||||
// Update referrer stats
|
||||
referrer := r.Referer()
|
||||
if referrer != "" {
|
||||
if stats.ReferrerStats == nil {
|
||||
stats.ReferrerStats = make(map[string]int)
|
||||
}
|
||||
stats.ReferrerStats[referrer]++
|
||||
}
|
||||
|
||||
// Update active hours
|
||||
hour := time.Now().Hour()
|
||||
found := false
|
||||
for i, h := range stats.MostActiveHours {
|
||||
if h.Hour == hour {
|
||||
stats.MostActiveHours[i].Count++
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
stats.MostActiveHours = append(stats.MostActiveHours, struct {
|
||||
Hour int `json:"hour"`
|
||||
Count int `json:"count"`
|
||||
}{Hour: hour, Count: 1})
|
||||
}
|
||||
|
||||
// Update active days
|
||||
day := time.Now().Weekday().String()
|
||||
found = false
|
||||
for i, d := range stats.MostActiveDays {
|
||||
if d.Day == day {
|
||||
stats.MostActiveDays[i].Count++
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
stats.MostActiveDays = append(stats.MostActiveDays, struct {
|
||||
Day string `json:"day"`
|
||||
Count int `json:"count"`
|
||||
}{Day: day, Count: 1})
|
||||
}
|
||||
|
||||
// Save visitor stats
|
||||
if err := saveVisitorStats(stats); err != nil {
|
||||
log.Printf("Error saving visitor stats: %v", err)
|
||||
@@ -218,41 +275,31 @@ func detectBrowser(userAgent string) string {
|
||||
return "Internet Explorer"
|
||||
} else if strings.Contains(userAgent, "edge") {
|
||||
return "Edge"
|
||||
} else if strings.Contains(userAgent, "opera") {
|
||||
return "Opera"
|
||||
} else {
|
||||
return "Unknown"
|
||||
}
|
||||
userAgent = strings.ToLower(userAgent)
|
||||
if strings.Contains(userAgent, "chrome") {
|
||||
return "Chrome"
|
||||
} else if strings.Contains(userAgent, "safari") && !strings.Contains(userAgent, "chrome") {
|
||||
return "Safari"
|
||||
} else if strings.Contains(userAgent, "firefox") {
|
||||
return "Firefox"
|
||||
} else if strings.Contains(userAgent, "msie") || strings.Contains(userAgent, "trident") {
|
||||
return "Internet Explorer"
|
||||
} else if strings.Contains(userAgent, "edge") {
|
||||
return "Edge"
|
||||
} else {
|
||||
return "Unknown"
|
||||
}
|
||||
}
|
||||
|
||||
// Helper function to extract OS from User-Agent
|
||||
func detectOS(userAgent string) string {
|
||||
userAgent = strings.ToLower(userAgent)
|
||||
if strings.Contains(userAgent, "windows") {
|
||||
return "Windows"
|
||||
} else if strings.Contains(userAgent, "mac os") {
|
||||
return "MacOS"
|
||||
} else if strings.Contains(userAgent, "linux") {
|
||||
return "Linux"
|
||||
} else if strings.Contains(userAgent, "android") {
|
||||
return "Android"
|
||||
} else if strings.Contains(userAgent, "ios") {
|
||||
return "iOS"
|
||||
} else {
|
||||
return "Unknown"
|
||||
}
|
||||
userAgent = strings.ToLower(userAgent)
|
||||
if strings.Contains(userAgent, "windows") {
|
||||
return "Windows"
|
||||
} else if strings.Contains(userAgent, "mac os") || strings.Contains(userAgent, "macintosh") {
|
||||
return "macOS"
|
||||
} else if strings.Contains(userAgent, "iphone") || strings.Contains(userAgent, "ipad") || strings.Contains(userAgent, "ipod") {
|
||||
return "iOS"
|
||||
} else if strings.Contains(userAgent, "android") {
|
||||
return "Android"
|
||||
} else if strings.Contains(userAgent, "linux") {
|
||||
return "Linux"
|
||||
} else if strings.Contains(userAgent, "bsd") {
|
||||
return "BSD"
|
||||
} else {
|
||||
return "Unknown"
|
||||
}
|
||||
}
|
||||
|
||||
// Helper function to extract OS from User-Agent
|
||||
|
||||
Reference in New Issue
Block a user