fix auth middleware and statistics issues

This commit is contained in:
Fran Jurmanović
2025-06-26 01:56:49 +02:00
parent 74df36cd0d
commit 7fdda06dba
9 changed files with 37 additions and 42 deletions

View File

@@ -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
})

View File

@@ -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"})
}

View File

@@ -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)

View File

@@ -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"})
}

View File

@@ -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"`

View File

@@ -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

View File

@@ -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,

View File

@@ -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

View File

@@ -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),
},