mirror of
https://github.com/Dvorinka/MyClubServer.git
synced 2026-06-04 02:32:57 +00:00
hot fix #1
This commit is contained in:
@@ -35,13 +35,13 @@ func (ac *AnalyticsController) resolveWebsiteID() (string, error) {
|
||||
if id := strings.TrimSpace(config.AppConfig.UmamiWebsiteID); id != "" {
|
||||
return id, nil
|
||||
}
|
||||
|
||||
|
||||
// Try to get the first available website
|
||||
id, err := ac.umamiService.GetDefaultWebsiteID()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
|
||||
config.AppConfig.UmamiWebsiteID = id
|
||||
return id, nil
|
||||
}
|
||||
@@ -54,13 +54,13 @@ func getClientIP(c *gin.Context) string {
|
||||
ips := strings.Split(xff, ",")
|
||||
return strings.TrimSpace(ips[0])
|
||||
}
|
||||
|
||||
|
||||
// Check X-Real-IP header
|
||||
xri := c.GetHeader("X-Real-IP")
|
||||
if xri != "" {
|
||||
return xri
|
||||
}
|
||||
|
||||
|
||||
// Fall back to RemoteAddr
|
||||
return c.ClientIP()
|
||||
}
|
||||
@@ -108,32 +108,32 @@ func (ac *AnalyticsController) GetAnalytics(c *gin.Context) {
|
||||
// Get users stats
|
||||
var totalUsers int64
|
||||
ac.DB.Model(&models.User{}).Count(&totalUsers)
|
||||
|
||||
|
||||
// Get new users this week
|
||||
weekAgo := time.Now().AddDate(0, 0, -7)
|
||||
var newUsersThisWeek int64
|
||||
ac.DB.Model(&models.User{}).Where("created_at >= ?", weekAgo).Count(&newUsersThisWeek)
|
||||
|
||||
|
||||
// Get events stats
|
||||
var totalEvents int64
|
||||
ac.DB.Model(&models.Event{}).Count(&totalEvents)
|
||||
|
||||
|
||||
// Get upcoming events (events with start_time in the future)
|
||||
now := time.Now()
|
||||
var upcomingEvents int64
|
||||
ac.DB.Model(&models.Event{}).Where("start_time > ?", now).Count(&upcomingEvents)
|
||||
|
||||
|
||||
// Get articles stats
|
||||
var totalArticles int64
|
||||
ac.DB.Model(&models.Article{}).Count(&totalArticles)
|
||||
|
||||
|
||||
var publishedArticles int64
|
||||
ac.DB.Model(&models.Article{}).Where("published = ?", true).Count(&publishedArticles)
|
||||
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"users": gin.H{
|
||||
"total": totalUsers,
|
||||
"new_this_week": newUsersThisWeek,
|
||||
"total": totalUsers,
|
||||
"new_this_week": newUsersThisWeek,
|
||||
},
|
||||
"events": gin.H{
|
||||
"total": totalEvents,
|
||||
@@ -155,18 +155,18 @@ func (ac *AnalyticsController) GetVisitors(c *gin.Context) {
|
||||
days = parsed
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
groupBy := c.DefaultQuery("groupBy", "day")
|
||||
startDate := time.Now().AddDate(0, 0, -days)
|
||||
|
||||
|
||||
type VisitorStat struct {
|
||||
Date string `json:"date"`
|
||||
PageViews int64 `json:"pageViews"`
|
||||
UniqueVisitors int64 `json:"uniqueVisitors"`
|
||||
}
|
||||
|
||||
|
||||
var stats []VisitorStat
|
||||
|
||||
|
||||
// Group by date
|
||||
if groupBy == "day" {
|
||||
ac.DB.Model(&models.VisitorEvent{}).
|
||||
@@ -184,12 +184,11 @@ func (ac *AnalyticsController) GetVisitors(c *gin.Context) {
|
||||
Order("date ASC").
|
||||
Scan(&stats)
|
||||
}
|
||||
|
||||
|
||||
// Transform data for chart format
|
||||
labels := make([]string, 0, len(stats))
|
||||
pageViewsData := make([]int64, 0, len(stats))
|
||||
uniqueVisitorsData := make([]int64, 0, len(stats))
|
||||
|
||||
|
||||
var totalVisitors int64
|
||||
for _, stat := range stats {
|
||||
// Format date as "d. M." (e.g., "5. 10.")
|
||||
@@ -199,11 +198,10 @@ func (ac *AnalyticsController) GetVisitors(c *gin.Context) {
|
||||
} else {
|
||||
labels = append(labels, stat.Date)
|
||||
}
|
||||
pageViewsData = append(pageViewsData, stat.PageViews)
|
||||
uniqueVisitorsData = append(uniqueVisitorsData, stat.UniqueVisitors)
|
||||
totalVisitors += stat.UniqueVisitors
|
||||
}
|
||||
|
||||
|
||||
// Calculate change percentage (compare last 7 days with previous 7 days)
|
||||
var changePercentage float64
|
||||
if len(stats) >= 14 {
|
||||
@@ -218,39 +216,39 @@ func (ac *AnalyticsController) GetVisitors(c *gin.Context) {
|
||||
changePercentage = float64(recentSum-previousSum) / float64(previousSum) * 100
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
response := gin.H{
|
||||
"totalVisitors": totalVisitors,
|
||||
"totalVisitors": totalVisitors,
|
||||
"changePercentage": changePercentage,
|
||||
"chartData": gin.H{
|
||||
"labels": labels,
|
||||
"datasets": []gin.H{
|
||||
{
|
||||
"label": "Návštěvníci",
|
||||
"data": uniqueVisitorsData,
|
||||
"borderColor": "rgba(66, 153, 225, 1)",
|
||||
"label": "Návštěvníci",
|
||||
"data": uniqueVisitorsData,
|
||||
"borderColor": "rgba(66, 153, 225, 1)",
|
||||
"backgroundColor": "rgba(66, 153, 225, 0.5)",
|
||||
"tension": 0.3,
|
||||
"fill": true,
|
||||
"tension": 0.3,
|
||||
"fill": true,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
c.JSON(http.StatusOK, response)
|
||||
}
|
||||
|
||||
// GetAnalyticsOverview returns overview statistics for admin dashboard
|
||||
func (ac *AnalyticsController) GetAnalyticsOverview(c *gin.Context) {
|
||||
var totalPageViews, uniqueVisitors, pageViewsToday, pageViewsWeek, uniqueVisitorsWeek int64
|
||||
|
||||
|
||||
// Try to fetch from Umami first
|
||||
websiteID, err := ac.resolveWebsiteID()
|
||||
if err == nil && websiteID != "" {
|
||||
// Fetch overall stats (last 365 days for total)
|
||||
endAt := time.Now().Unix() * 1000
|
||||
startAt := time.Now().AddDate(-1, 0, 0).Unix() * 1000
|
||||
|
||||
|
||||
stats, err := ac.umamiService.GetWebsiteStats(websiteID, startAt, endAt)
|
||||
if err == nil {
|
||||
if pv, ok := stats["pageviews"].(map[string]interface{}); ok {
|
||||
@@ -264,9 +262,9 @@ func (ac *AnalyticsController) GetAnalyticsOverview(c *gin.Context) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Fetch today's stats
|
||||
todayStart := time.Now().Truncate(24 * time.Hour).Unix() * 1000
|
||||
todayStart := time.Now().Truncate(24*time.Hour).Unix() * 1000
|
||||
todayEnd := time.Now().Unix() * 1000
|
||||
todayStats, err := ac.umamiService.GetWebsiteStats(websiteID, todayStart, todayEnd)
|
||||
if err == nil {
|
||||
@@ -276,7 +274,7 @@ func (ac *AnalyticsController) GetAnalyticsOverview(c *gin.Context) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Fetch this week's stats
|
||||
weekStart := time.Now().AddDate(0, 0, -7).Unix() * 1000
|
||||
weekEnd := time.Now().Unix() * 1000
|
||||
@@ -297,10 +295,10 @@ func (ac *AnalyticsController) GetAnalyticsOverview(c *gin.Context) {
|
||||
// Fallback to internal analytics if Umami is not available
|
||||
ac.DB.Model(&models.VisitorEvent{}).Where("event_type = ?", "page_view").Count(&totalPageViews)
|
||||
ac.DB.Model(&models.VisitorEvent{}).Where("event_type = ?", "page_view").Distinct("ip_address").Count(&uniqueVisitors)
|
||||
|
||||
|
||||
today := time.Now().Format("2006-01-02")
|
||||
ac.DB.Model(&models.VisitorEvent{}).Where("event_type = ? AND DATE(created_at) = ?", "page_view", today).Count(&pageViewsToday)
|
||||
|
||||
|
||||
weekAgo := time.Now().AddDate(0, 0, -7)
|
||||
ac.DB.Model(&models.VisitorEvent{}).Where("event_type = ? AND created_at >= ?", "page_view", weekAgo).Count(&pageViewsWeek)
|
||||
ac.DB.Model(&models.VisitorEvent{}).Where("event_type = ? AND created_at >= ?", "page_view", weekAgo).Distinct("ip_address").Count(&uniqueVisitorsWeek)
|
||||
@@ -312,13 +310,13 @@ func (ac *AnalyticsController) GetAnalyticsOverview(c *gin.Context) {
|
||||
ac.DB.Model(&models.Article{}).Where("published = ?", true).Count(&publishedArticles)
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"total_page_views": totalPageViews,
|
||||
"unique_visitors": uniqueVisitors,
|
||||
"total_articles": totalArticles,
|
||||
"published_articles": publishedArticles,
|
||||
"page_views_today": pageViewsToday,
|
||||
"page_views_week": pageViewsWeek,
|
||||
"unique_visitors_week": uniqueVisitorsWeek,
|
||||
"total_page_views": totalPageViews,
|
||||
"unique_visitors": uniqueVisitors,
|
||||
"total_articles": totalArticles,
|
||||
"published_articles": publishedArticles,
|
||||
"page_views_today": pageViewsToday,
|
||||
"page_views_week": pageViewsWeek,
|
||||
"unique_visitors_week": uniqueVisitorsWeek,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -337,14 +335,14 @@ func (ac *AnalyticsController) GetTopPages(c *gin.Context) {
|
||||
}
|
||||
|
||||
var pages []PageStats
|
||||
|
||||
|
||||
// Try to fetch from Umami first
|
||||
websiteID, err := ac.resolveWebsiteID()
|
||||
if err == nil && websiteID != "" {
|
||||
// Fetch URL metrics from Umami (last 30 days)
|
||||
endAt := time.Now().Unix() * 1000
|
||||
startAt := time.Now().AddDate(0, 0, -30).Unix() * 1000
|
||||
|
||||
|
||||
metrics, err := ac.umamiService.GetWebsiteMetrics(websiteID, "url", startAt, endAt)
|
||||
if err == nil && metrics != nil {
|
||||
// Convert Umami metrics to PageStats format
|
||||
@@ -354,14 +352,14 @@ func (ac *AnalyticsController) GetTopPages(c *gin.Context) {
|
||||
}
|
||||
pagePath := ""
|
||||
viewCount := int64(0)
|
||||
|
||||
|
||||
if x, ok := metricMap["x"].(string); ok {
|
||||
pagePath = x
|
||||
}
|
||||
if y, ok := metricMap["y"].(float64); ok {
|
||||
viewCount = int64(y)
|
||||
}
|
||||
|
||||
|
||||
pages = append(pages, PageStats{
|
||||
PagePath: pagePath,
|
||||
PageName: pagePath,
|
||||
@@ -373,7 +371,7 @@ func (ac *AnalyticsController) GetTopPages(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Fallback to internal analytics
|
||||
ac.DB.Model(&models.VisitorEvent{}).
|
||||
Where("event_type = ?", "page_view").
|
||||
@@ -403,33 +401,37 @@ func (ac *AnalyticsController) GetTopArticles(c *gin.Context) {
|
||||
}
|
||||
|
||||
type TopInteraction struct {
|
||||
Page string `json:"page"`
|
||||
Element string `json:"element"`
|
||||
Count int64 `json:"count"`
|
||||
Page string `json:"page"`
|
||||
Element string `json:"element"`
|
||||
Count int64 `json:"count"`
|
||||
}
|
||||
|
||||
func (ctrl *AnalyticsController) GetTopInteractions(c *gin.Context) {
|
||||
daysParam := c.DefaultQuery("days", "30")
|
||||
limitParam := c.DefaultQuery("limit", "10")
|
||||
days, _ := strconv.Atoi(daysParam)
|
||||
if days <= 0 || days > 365 { days = 30 }
|
||||
limit, _ := strconv.Atoi(limitParam)
|
||||
if limit <= 0 || limit > 100 { limit = 10 }
|
||||
daysParam := c.DefaultQuery("days", "30")
|
||||
limitParam := c.DefaultQuery("limit", "10")
|
||||
days, _ := strconv.Atoi(daysParam)
|
||||
if days <= 0 || days > 365 {
|
||||
days = 30
|
||||
}
|
||||
limit, _ := strconv.Atoi(limitParam)
|
||||
if limit <= 0 || limit > 100 {
|
||||
limit = 10
|
||||
}
|
||||
|
||||
start := time.Now().AddDate(0, 0, -days)
|
||||
start := time.Now().AddDate(0, 0, -days)
|
||||
|
||||
var rows []TopInteraction
|
||||
err := ctrl.DB.
|
||||
Model(&models.VisitorEvent{}).
|
||||
Select("page, element, COUNT(*) as count").
|
||||
Where("event_type IN ? AND created_at >= ?", []string{"click", "interaction"}, start).
|
||||
Group("page, element").
|
||||
Order("count DESC").
|
||||
Limit(limit).
|
||||
Scan(&rows).Error
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to load interactions"})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{"items": rows})
|
||||
var rows []TopInteraction
|
||||
err := ctrl.DB.
|
||||
Model(&models.VisitorEvent{}).
|
||||
Select("page, element, COUNT(*) as count").
|
||||
Where("event_type IN ? AND created_at >= ?", []string{"click", "interaction"}, start).
|
||||
Group("page, element").
|
||||
Order("count DESC").
|
||||
Limit(limit).
|
||||
Scan(&rows).Error
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to load interactions"})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{"items": rows})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user