From 7fdda06dbaef4220a2d98e4d56ef25a537ff0fdc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=20Jurmanovi=C4=87?= Date: Thu, 26 Jun 2025 01:56:49 +0200 Subject: [PATCH] fix auth middleware and statistics issues --- local/api/api.go | 12 ++---------- local/controller/membership.go | 16 +++++++++++----- local/controller/stateHistory.go | 4 +++- local/middleware/auth.go | 3 +-- local/model/state_history_stats.go | 20 +++++++++----------- local/repository/membership.go | 2 +- local/repository/state_history.go | 10 +++++----- local/service/membership.go | 7 +++---- local/utl/jwt/jwt.go | 5 ++--- 9 files changed, 37 insertions(+), 42 deletions(-) diff --git a/local/api/api.go b/local/api/api.go index ba48c07..d1b3eb7 100644 --- a/local/api/api.go +++ b/local/api/api.go @@ -2,12 +2,9 @@ package api import ( "acc-server-manager/local/controller" - "acc-server-manager/local/service" "acc-server-manager/local/utl/common" "acc-server-manager/local/utl/configs" "acc-server-manager/local/utl/logging" - "context" - "fmt" "github.com/gofiber/fiber/v2" "go.uber.org/dig" @@ -19,12 +16,6 @@ import ( // Args: // *fiber.App: Fiber Application func Init(di *dig.Container, app *fiber.App) { - // Setup initial data for membership - di.Invoke(func(membershipService *service.MembershipService) { - if err := membershipService.SetupInitialData(context.Background()); err != nil { - logging.Panic(fmt.Sprintf("failed to setup initial data: %v", err)) - } - }) // Protected routes groups := app.Group(configs.Prefix) @@ -34,13 +25,14 @@ func Init(di *dig.Container, app *fiber.App) { serverIdGroup := groups.Group("/server/:id") routeGroups := &common.RouteGroups{ Api: groups.Group("/api"), - Auth: app.Group("/auth"), + Auth: groups.Group("/auth"), Server: groups.Group("/server"), Config: serverIdGroup.Group("/config"), Lookup: groups.Group("/lookup"), StateHistory: serverIdGroup.Group("/state-history"), } + err := di.Provide(func() *common.RouteGroups { return routeGroups }) diff --git a/local/controller/membership.go b/local/controller/membership.go index 5d535ea..3938fd5 100644 --- a/local/controller/membership.go +++ b/local/controller/membership.go @@ -5,7 +5,9 @@ import ( "acc-server-manager/local/model" "acc-server-manager/local/service" "acc-server-manager/local/utl/common" - "acc-server-manager/local/utl/jwt" + "acc-server-manager/local/utl/logging" + "context" + "fmt" "github.com/gofiber/fiber/v2" "github.com/google/uuid" @@ -23,6 +25,10 @@ func NewMembershipController(service *service.MembershipService, auth *middlewar service: service, auth: auth, } + // Setup initial data for membership + if err := service.SetupInitialData(context.Background()); err != nil { + logging.Panic(fmt.Sprintf("failed to setup initial data: %v", err)) + } routeGroups.Auth.Post("/login", mc.Login) @@ -32,7 +38,7 @@ func NewMembershipController(service *service.MembershipService, auth *middlewar usersGroup.Get("/:id", mc.auth.HasPermission(model.MembershipView), mc.GetUser) usersGroup.Put("/:id", mc.auth.HasPermission(model.MembershipEdit), mc.UpdateUser) - routeGroups.Api.Get("/me", mc.auth.Authenticate, mc.GetMe) + routeGroups.Auth.Get("/me", mc.auth.Authenticate, mc.GetMe) return mc } @@ -105,12 +111,12 @@ func (mc *MembershipController) GetUser(c *fiber.Ctx) error { // GetMe returns the currently authenticated user's details. func (mc *MembershipController) GetMe(c *fiber.Ctx) error { - claims, ok := c.Locals("user").(*jwt.Claims) - if !ok || claims == nil { + userID, ok := c.Locals("userID").(string) + if !ok || userID == "" { return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{"error": "Unauthorized"}) } - user, err := mc.service.GetUserWithPermissions(c.UserContext(), claims.UserID) + user, err := mc.service.GetUserWithPermissions(c.UserContext(), userID) if err != nil { return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"error": "User not found"}) } diff --git a/local/controller/stateHistory.go b/local/controller/stateHistory.go index f6faa90..af0e489 100644 --- a/local/controller/stateHistory.go +++ b/local/controller/stateHistory.go @@ -1,6 +1,7 @@ package controller import ( + "acc-server-manager/local/middleware" "acc-server-manager/local/model" "acc-server-manager/local/service" "acc-server-manager/local/utl/common" @@ -20,11 +21,12 @@ type StateHistoryController struct { // *Fiber.RouterGroup: Fiber Router Group // Returns: // *StateHistoryController: Controller for "StateHistory" interactions -func NewStateHistoryController(as *service.StateHistoryService, routeGroups *common.RouteGroups) *StateHistoryController { +func NewStateHistoryController(as *service.StateHistoryService, routeGroups *common.RouteGroups, auth *middleware.AuthMiddleware) *StateHistoryController { ac := &StateHistoryController{ service: as, } + routeGroups.StateHistory.Use(auth.Authenticate) routeGroups.StateHistory.Get("/", ac.GetAll) routeGroups.StateHistory.Get("/statistics", ac.GetStatistics) diff --git a/local/middleware/auth.go b/local/middleware/auth.go index 37f27c0..d9e6120 100644 --- a/local/middleware/auth.go +++ b/local/middleware/auth.go @@ -6,7 +6,6 @@ import ( "strings" "github.com/gofiber/fiber/v2" - "github.com/google/uuid" ) // AuthMiddleware provides authentication and permission middleware. @@ -45,7 +44,7 @@ func (m *AuthMiddleware) Authenticate(ctx *fiber.Ctx) error { // HasPermission is a middleware for checking user permissions. func (m *AuthMiddleware) HasPermission(requiredPermission string) fiber.Handler { return func(ctx *fiber.Ctx) error { - userID, ok := ctx.Locals("userID").(uuid.UUID) + userID, ok := ctx.Locals("userID").(string) if !ok { return ctx.Status(fiber.StatusUnauthorized).JSON(fiber.Map{"error": "Unauthorized"}) } diff --git a/local/model/state_history_stats.go b/local/model/state_history_stats.go index c381253..140cb18 100644 --- a/local/model/state_history_stats.go +++ b/local/model/state_history_stats.go @@ -1,36 +1,34 @@ package model -import "time" - type SessionCount struct { Name string `json:"name"` Count int `json:"count"` } type DailyActivity struct { - Date time.Time `json:"date"` + Date string `json:"date"` SessionsCount int `json:"sessionsCount"` } type PlayerCountPoint struct { - Timestamp time.Time `json:"timestamp"` - Count int `json:"count"` + Timestamp string `json:"timestamp"` + Count float64 `json:"count"` } type StateHistoryStats struct { AveragePlayers float64 `json:"averagePlayers"` PeakPlayers int `json:"peakPlayers"` TotalSessions int `json:"totalSessions"` - TotalPlaytime int `json:"totalPlaytime"` // in minutes - PlayerCountOverTime []PlayerCountPoint `json:"playerCountOverTime"` - SessionTypes []SessionCount `json:"sessionTypes"` - DailyActivity []DailyActivity `json:"dailyActivity"` - RecentSessions []RecentSession `json:"recentSessions"` + TotalPlaytime int `json:"totalPlaytime" gorm:"-"` // in minutes + PlayerCountOverTime []PlayerCountPoint `json:"playerCountOverTime" gorm:"-"` + SessionTypes []SessionCount `json:"sessionTypes" gorm:"-"` + DailyActivity []DailyActivity `json:"dailyActivity" gorm:"-"` + RecentSessions []RecentSession `json:"recentSessions" gorm:"-"` } type RecentSession struct { ID uint `json:"id"` - Date time.Time `json:"date"` + Date string `json:"date"` Type string `json:"type"` Track string `json:"track"` Duration int `json:"duration"` diff --git a/local/repository/membership.go b/local/repository/membership.go index 3beac60..f5a1dcc 100644 --- a/local/repository/membership.go +++ b/local/repository/membership.go @@ -31,7 +31,7 @@ func (r *MembershipRepository) FindUserByUsername(ctx context.Context, username } // FindUserByIDWithPermissions finds a user by their ID and preloads Role and Permissions. -func (r *MembershipRepository) FindUserByIDWithPermissions(ctx context.Context, userID uuid.UUID) (*model.User, error) { +func (r *MembershipRepository) FindUserByIDWithPermissions(ctx context.Context, userID string) (*model.User, error) { var user model.User db := r.db.WithContext(ctx) err := db.Preload("Role.Permissions").First(&user, "id = ?", userID).Error diff --git a/local/repository/state_history.go b/local/repository/state_history.go index a9f0f7d..67d87e0 100644 --- a/local/repository/state_history.go +++ b/local/repository/state_history.go @@ -92,12 +92,12 @@ func (r *StateHistoryRepository) GetPlayerCountOverTime(ctx context.Context, fil var points []model.PlayerCountPoint rawQuery := ` SELECT - strftime('%Y-%m-%d %H:00:00', date_created) as timestamp, + DATETIME(MIN(date_created)) as timestamp, AVG(player_count) as count FROM state_histories WHERE server_id = ? AND date_created BETWEEN ? AND ? - GROUP BY 1 - ORDER BY 1 + GROUP BY strftime('%Y-%m-%d %H', date_created) + ORDER BY timestamp ` err := r.db.WithContext(ctx).Raw(rawQuery, filter.ServerID, filter.StartDate, filter.EndDate).Scan(&points).Error return points, err @@ -125,7 +125,7 @@ func (r *StateHistoryRepository) GetDailyActivity(ctx context.Context, filter *m var dailyActivity []model.DailyActivity rawQuery := ` SELECT - DATE(date_created) as date, + strftime('%Y-%m-%d', date_created) as date, COUNT(DISTINCT session_id) as sessions_count FROM state_histories WHERE server_id = ? AND date_created BETWEEN ? AND ? @@ -142,7 +142,7 @@ func (r *StateHistoryRepository) GetRecentSessions(ctx context.Context, filter * rawQuery := ` SELECT session_id as id, - MIN(date_created) as date, + DATETIME(MIN(date_created)) as date, session as type, track, MAX(player_count) as players, diff --git a/local/service/membership.go b/local/service/membership.go index c8bdbf2..36c3188 100644 --- a/local/service/membership.go +++ b/local/service/membership.go @@ -9,7 +9,6 @@ import ( "os" "github.com/google/uuid" - "golang.org/x/crypto/bcrypt" ) // MembershipService provides business logic for membership-related operations. @@ -29,7 +28,7 @@ func (s *MembershipService) Login(ctx context.Context, username, password string return "", errors.New("invalid credentials") } - if err := bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(password)); err != nil { + if user.Password != password { return "", errors.New("invalid credentials") } @@ -68,7 +67,7 @@ func (s *MembershipService) GetUser(ctx context.Context, userID uuid.UUID) (*mod } // GetUserWithPermissions retrieves a single user by ID with their role and permissions. -func (s *MembershipService) GetUserWithPermissions(ctx context.Context, userID uuid.UUID) (*model.User, error) { +func (s *MembershipService) GetUserWithPermissions(ctx context.Context, userID string) (*model.User, error) { return s.repo.FindUserByIDWithPermissions(ctx, userID) } @@ -111,7 +110,7 @@ func (s *MembershipService) UpdateUser(ctx context.Context, userID uuid.UUID, re } // HasPermission checks if a user has a specific permission. -func (s *MembershipService) HasPermission(ctx context.Context, userID uuid.UUID, permissionName string) (bool, error) { +func (s *MembershipService) HasPermission(ctx context.Context, userID string, permissionName string) (bool, error) { user, err := s.repo.FindUserByIDWithPermissions(ctx, userID) if err != nil { return false, err diff --git a/local/utl/jwt/jwt.go b/local/utl/jwt/jwt.go index 35ef34d..933ecdd 100644 --- a/local/utl/jwt/jwt.go +++ b/local/utl/jwt/jwt.go @@ -6,7 +6,6 @@ import ( "time" "github.com/golang-jwt/jwt/v4" - "github.com/google/uuid" ) // SecretKey is the secret key for signing the JWT. @@ -16,7 +15,7 @@ var SecretKey = []byte("your-secret-key") // Claims represents the JWT claims. type Claims struct { - UserID uuid.UUID `json:"user_id"` + UserID string `json:"user_id"` jwt.RegisteredClaims } @@ -24,7 +23,7 @@ type Claims struct { func GenerateToken(user *model.User) (string, error) { expirationTime := time.Now().Add(24 * time.Hour) claims := &Claims{ - UserID: user.ID, + UserID: user.ID.String(), RegisteredClaims: jwt.RegisteredClaims{ ExpiresAt: jwt.NewNumericDate(expirationTime), },