fix auth middleware and statistics issues
This commit is contained in:
@@ -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
|
||||
})
|
||||
|
||||
@@ -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"})
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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"})
|
||||
}
|
||||
|
||||
@@ -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"`
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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),
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user