fix auth middleware and statistics issues
This commit is contained in:
@@ -2,12 +2,9 @@ package api
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"acc-server-manager/local/controller"
|
"acc-server-manager/local/controller"
|
||||||
"acc-server-manager/local/service"
|
|
||||||
"acc-server-manager/local/utl/common"
|
"acc-server-manager/local/utl/common"
|
||||||
"acc-server-manager/local/utl/configs"
|
"acc-server-manager/local/utl/configs"
|
||||||
"acc-server-manager/local/utl/logging"
|
"acc-server-manager/local/utl/logging"
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
"go.uber.org/dig"
|
"go.uber.org/dig"
|
||||||
@@ -19,12 +16,6 @@ import (
|
|||||||
// Args:
|
// Args:
|
||||||
// *fiber.App: Fiber Application
|
// *fiber.App: Fiber Application
|
||||||
func Init(di *dig.Container, app *fiber.App) {
|
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
|
// Protected routes
|
||||||
groups := app.Group(configs.Prefix)
|
groups := app.Group(configs.Prefix)
|
||||||
@@ -34,13 +25,14 @@ func Init(di *dig.Container, app *fiber.App) {
|
|||||||
serverIdGroup := groups.Group("/server/:id")
|
serverIdGroup := groups.Group("/server/:id")
|
||||||
routeGroups := &common.RouteGroups{
|
routeGroups := &common.RouteGroups{
|
||||||
Api: groups.Group("/api"),
|
Api: groups.Group("/api"),
|
||||||
Auth: app.Group("/auth"),
|
Auth: groups.Group("/auth"),
|
||||||
Server: groups.Group("/server"),
|
Server: groups.Group("/server"),
|
||||||
Config: serverIdGroup.Group("/config"),
|
Config: serverIdGroup.Group("/config"),
|
||||||
Lookup: groups.Group("/lookup"),
|
Lookup: groups.Group("/lookup"),
|
||||||
StateHistory: serverIdGroup.Group("/state-history"),
|
StateHistory: serverIdGroup.Group("/state-history"),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
err := di.Provide(func() *common.RouteGroups {
|
err := di.Provide(func() *common.RouteGroups {
|
||||||
return routeGroups
|
return routeGroups
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -5,7 +5,9 @@ import (
|
|||||||
"acc-server-manager/local/model"
|
"acc-server-manager/local/model"
|
||||||
"acc-server-manager/local/service"
|
"acc-server-manager/local/service"
|
||||||
"acc-server-manager/local/utl/common"
|
"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/gofiber/fiber/v2"
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
@@ -23,6 +25,10 @@ func NewMembershipController(service *service.MembershipService, auth *middlewar
|
|||||||
service: service,
|
service: service,
|
||||||
auth: auth,
|
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)
|
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.Get("/:id", mc.auth.HasPermission(model.MembershipView), mc.GetUser)
|
||||||
usersGroup.Put("/:id", mc.auth.HasPermission(model.MembershipEdit), mc.UpdateUser)
|
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
|
return mc
|
||||||
}
|
}
|
||||||
@@ -105,12 +111,12 @@ func (mc *MembershipController) GetUser(c *fiber.Ctx) error {
|
|||||||
|
|
||||||
// GetMe returns the currently authenticated user's details.
|
// GetMe returns the currently authenticated user's details.
|
||||||
func (mc *MembershipController) GetMe(c *fiber.Ctx) error {
|
func (mc *MembershipController) GetMe(c *fiber.Ctx) error {
|
||||||
claims, ok := c.Locals("user").(*jwt.Claims)
|
userID, ok := c.Locals("userID").(string)
|
||||||
if !ok || claims == nil {
|
if !ok || userID == "" {
|
||||||
return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{"error": "Unauthorized"})
|
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 {
|
if err != nil {
|
||||||
return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"error": "User not found"})
|
return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"error": "User not found"})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package controller
|
package controller
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"acc-server-manager/local/middleware"
|
||||||
"acc-server-manager/local/model"
|
"acc-server-manager/local/model"
|
||||||
"acc-server-manager/local/service"
|
"acc-server-manager/local/service"
|
||||||
"acc-server-manager/local/utl/common"
|
"acc-server-manager/local/utl/common"
|
||||||
@@ -20,11 +21,12 @@ type StateHistoryController struct {
|
|||||||
// *Fiber.RouterGroup: Fiber Router Group
|
// *Fiber.RouterGroup: Fiber Router Group
|
||||||
// Returns:
|
// Returns:
|
||||||
// *StateHistoryController: Controller for "StateHistory" interactions
|
// *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{
|
ac := &StateHistoryController{
|
||||||
service: as,
|
service: as,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
routeGroups.StateHistory.Use(auth.Authenticate)
|
||||||
routeGroups.StateHistory.Get("/", ac.GetAll)
|
routeGroups.StateHistory.Get("/", ac.GetAll)
|
||||||
routeGroups.StateHistory.Get("/statistics", ac.GetStatistics)
|
routeGroups.StateHistory.Get("/statistics", ac.GetStatistics)
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
"github.com/google/uuid"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// AuthMiddleware provides authentication and permission middleware.
|
// 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.
|
// HasPermission is a middleware for checking user permissions.
|
||||||
func (m *AuthMiddleware) HasPermission(requiredPermission string) fiber.Handler {
|
func (m *AuthMiddleware) HasPermission(requiredPermission string) fiber.Handler {
|
||||||
return func(ctx *fiber.Ctx) error {
|
return func(ctx *fiber.Ctx) error {
|
||||||
userID, ok := ctx.Locals("userID").(uuid.UUID)
|
userID, ok := ctx.Locals("userID").(string)
|
||||||
if !ok {
|
if !ok {
|
||||||
return ctx.Status(fiber.StatusUnauthorized).JSON(fiber.Map{"error": "Unauthorized"})
|
return ctx.Status(fiber.StatusUnauthorized).JSON(fiber.Map{"error": "Unauthorized"})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,36 +1,34 @@
|
|||||||
package model
|
package model
|
||||||
|
|
||||||
import "time"
|
|
||||||
|
|
||||||
type SessionCount struct {
|
type SessionCount struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Count int `json:"count"`
|
Count int `json:"count"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type DailyActivity struct {
|
type DailyActivity struct {
|
||||||
Date time.Time `json:"date"`
|
Date string `json:"date"`
|
||||||
SessionsCount int `json:"sessionsCount"`
|
SessionsCount int `json:"sessionsCount"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type PlayerCountPoint struct {
|
type PlayerCountPoint struct {
|
||||||
Timestamp time.Time `json:"timestamp"`
|
Timestamp string `json:"timestamp"`
|
||||||
Count int `json:"count"`
|
Count float64 `json:"count"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type StateHistoryStats struct {
|
type StateHistoryStats struct {
|
||||||
AveragePlayers float64 `json:"averagePlayers"`
|
AveragePlayers float64 `json:"averagePlayers"`
|
||||||
PeakPlayers int `json:"peakPlayers"`
|
PeakPlayers int `json:"peakPlayers"`
|
||||||
TotalSessions int `json:"totalSessions"`
|
TotalSessions int `json:"totalSessions"`
|
||||||
TotalPlaytime int `json:"totalPlaytime"` // in minutes
|
TotalPlaytime int `json:"totalPlaytime" gorm:"-"` // in minutes
|
||||||
PlayerCountOverTime []PlayerCountPoint `json:"playerCountOverTime"`
|
PlayerCountOverTime []PlayerCountPoint `json:"playerCountOverTime" gorm:"-"`
|
||||||
SessionTypes []SessionCount `json:"sessionTypes"`
|
SessionTypes []SessionCount `json:"sessionTypes" gorm:"-"`
|
||||||
DailyActivity []DailyActivity `json:"dailyActivity"`
|
DailyActivity []DailyActivity `json:"dailyActivity" gorm:"-"`
|
||||||
RecentSessions []RecentSession `json:"recentSessions"`
|
RecentSessions []RecentSession `json:"recentSessions" gorm:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type RecentSession struct {
|
type RecentSession struct {
|
||||||
ID uint `json:"id"`
|
ID uint `json:"id"`
|
||||||
Date time.Time `json:"date"`
|
Date string `json:"date"`
|
||||||
Type string `json:"type"`
|
Type string `json:"type"`
|
||||||
Track string `json:"track"`
|
Track string `json:"track"`
|
||||||
Duration int `json:"duration"`
|
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.
|
// 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
|
var user model.User
|
||||||
db := r.db.WithContext(ctx)
|
db := r.db.WithContext(ctx)
|
||||||
err := db.Preload("Role.Permissions").First(&user, "id = ?", userID).Error
|
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
|
var points []model.PlayerCountPoint
|
||||||
rawQuery := `
|
rawQuery := `
|
||||||
SELECT
|
SELECT
|
||||||
strftime('%Y-%m-%d %H:00:00', date_created) as timestamp,
|
DATETIME(MIN(date_created)) as timestamp,
|
||||||
AVG(player_count) as count
|
AVG(player_count) as count
|
||||||
FROM state_histories
|
FROM state_histories
|
||||||
WHERE server_id = ? AND date_created BETWEEN ? AND ?
|
WHERE server_id = ? AND date_created BETWEEN ? AND ?
|
||||||
GROUP BY 1
|
GROUP BY strftime('%Y-%m-%d %H', date_created)
|
||||||
ORDER BY 1
|
ORDER BY timestamp
|
||||||
`
|
`
|
||||||
err := r.db.WithContext(ctx).Raw(rawQuery, filter.ServerID, filter.StartDate, filter.EndDate).Scan(&points).Error
|
err := r.db.WithContext(ctx).Raw(rawQuery, filter.ServerID, filter.StartDate, filter.EndDate).Scan(&points).Error
|
||||||
return points, err
|
return points, err
|
||||||
@@ -125,7 +125,7 @@ func (r *StateHistoryRepository) GetDailyActivity(ctx context.Context, filter *m
|
|||||||
var dailyActivity []model.DailyActivity
|
var dailyActivity []model.DailyActivity
|
||||||
rawQuery := `
|
rawQuery := `
|
||||||
SELECT
|
SELECT
|
||||||
DATE(date_created) as date,
|
strftime('%Y-%m-%d', date_created) as date,
|
||||||
COUNT(DISTINCT session_id) as sessions_count
|
COUNT(DISTINCT session_id) as sessions_count
|
||||||
FROM state_histories
|
FROM state_histories
|
||||||
WHERE server_id = ? AND date_created BETWEEN ? AND ?
|
WHERE server_id = ? AND date_created BETWEEN ? AND ?
|
||||||
@@ -142,7 +142,7 @@ func (r *StateHistoryRepository) GetRecentSessions(ctx context.Context, filter *
|
|||||||
rawQuery := `
|
rawQuery := `
|
||||||
SELECT
|
SELECT
|
||||||
session_id as id,
|
session_id as id,
|
||||||
MIN(date_created) as date,
|
DATETIME(MIN(date_created)) as date,
|
||||||
session as type,
|
session as type,
|
||||||
track,
|
track,
|
||||||
MAX(player_count) as players,
|
MAX(player_count) as players,
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"golang.org/x/crypto/bcrypt"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// MembershipService provides business logic for membership-related operations.
|
// 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")
|
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")
|
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.
|
// 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)
|
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.
|
// 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)
|
user, err := s.repo.FindUserByIDWithPermissions(ctx, userID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/golang-jwt/jwt/v4"
|
"github.com/golang-jwt/jwt/v4"
|
||||||
"github.com/google/uuid"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// SecretKey is the secret key for signing the JWT.
|
// SecretKey is the secret key for signing the JWT.
|
||||||
@@ -16,7 +15,7 @@ var SecretKey = []byte("your-secret-key")
|
|||||||
|
|
||||||
// Claims represents the JWT claims.
|
// Claims represents the JWT claims.
|
||||||
type Claims struct {
|
type Claims struct {
|
||||||
UserID uuid.UUID `json:"user_id"`
|
UserID string `json:"user_id"`
|
||||||
jwt.RegisteredClaims
|
jwt.RegisteredClaims
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -24,7 +23,7 @@ type Claims struct {
|
|||||||
func GenerateToken(user *model.User) (string, error) {
|
func GenerateToken(user *model.User) (string, error) {
|
||||||
expirationTime := time.Now().Add(24 * time.Hour)
|
expirationTime := time.Now().Add(24 * time.Hour)
|
||||||
claims := &Claims{
|
claims := &Claims{
|
||||||
UserID: user.ID,
|
UserID: user.ID.String(),
|
||||||
RegisteredClaims: jwt.RegisteredClaims{
|
RegisteredClaims: jwt.RegisteredClaims{
|
||||||
ExpiresAt: jwt.NewNumericDate(expirationTime),
|
ExpiresAt: jwt.NewNumericDate(expirationTime),
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user