add step list for server creation
All checks were successful
Release and Deploy / build (push) Successful in 9m5s
Release and Deploy / deploy (push) Successful in 26s

This commit is contained in:
Fran Jurmanović
2025-09-18 22:24:51 +02:00
parent 901dbe697e
commit 4004d83411
80 changed files with 950 additions and 2554 deletions

View File

@@ -7,13 +7,11 @@ import (
"gorm.io/gorm"
)
// BaseRepository provides generic CRUD operations for any model
type BaseRepository[T any, F any] struct {
db *gorm.DB
modelType T
}
// NewBaseRepository creates a new base repository for the given model type
func NewBaseRepository[T any, F any](db *gorm.DB, model T) *BaseRepository[T, F] {
return &BaseRepository[T, F]{
db: db,
@@ -21,23 +19,19 @@ func NewBaseRepository[T any, F any](db *gorm.DB, model T) *BaseRepository[T, F]
}
}
// GetAll retrieves all records based on the filter
func (r *BaseRepository[T, F]) GetAll(ctx context.Context, filter *F) (*[]T, error) {
result := new([]T)
query := r.db.WithContext(ctx).Model(&r.modelType)
// Apply filter conditions if filter implements Filterable
if filterable, ok := any(filter).(Filterable); ok {
query = filterable.ApplyFilter(query)
}
// Apply pagination if filter implements Pageable
if pageable, ok := any(filter).(Pageable); ok {
offset, limit := pageable.Pagination()
query = query.Offset(offset).Limit(limit)
}
// Apply sorting if filter implements Sortable
if sortable, ok := any(filter).(Sortable); ok {
field, desc := sortable.GetSorting()
if desc {
@@ -54,7 +48,6 @@ func (r *BaseRepository[T, F]) GetAll(ctx context.Context, filter *F) (*[]T, err
return result, nil
}
// GetByID retrieves a single record by ID
func (r *BaseRepository[T, F]) GetByID(ctx context.Context, id interface{}) (*T, error) {
result := new(T)
if err := r.db.WithContext(ctx).Where("id = ?", id).First(result).Error; err != nil {
@@ -66,7 +59,6 @@ func (r *BaseRepository[T, F]) GetByID(ctx context.Context, id interface{}) (*T,
return result, nil
}
// Insert creates a new record
func (r *BaseRepository[T, F]) Insert(ctx context.Context, model *T) error {
if err := r.db.WithContext(ctx).Create(model).Error; err != nil {
return fmt.Errorf("error creating record: %w", err)
@@ -74,7 +66,6 @@ func (r *BaseRepository[T, F]) Insert(ctx context.Context, model *T) error {
return nil
}
// Update modifies an existing record
func (r *BaseRepository[T, F]) Update(ctx context.Context, model *T) error {
if err := r.db.WithContext(ctx).Save(model).Error; err != nil {
return fmt.Errorf("error updating record: %w", err)
@@ -82,7 +73,6 @@ func (r *BaseRepository[T, F]) Update(ctx context.Context, model *T) error {
return nil
}
// Delete removes a record by ID
func (r *BaseRepository[T, F]) Delete(ctx context.Context, id interface{}) error {
if err := r.db.WithContext(ctx).Delete(new(T), id).Error; err != nil {
return fmt.Errorf("error deleting record: %w", err)
@@ -90,7 +80,6 @@ func (r *BaseRepository[T, F]) Delete(ctx context.Context, id interface{}) error
return nil
}
// Count returns the total number of records matching the filter
func (r *BaseRepository[T, F]) Count(ctx context.Context, filter *F) (int64, error) {
var count int64
query := r.db.WithContext(ctx).Model(&r.modelType)
@@ -106,8 +95,6 @@ func (r *BaseRepository[T, F]) Count(ctx context.Context, filter *F) (int64, err
return count, nil
}
// Interfaces for filter capabilities
type Filterable interface {
ApplyFilter(*gorm.DB) *gorm.DB
}

View File

@@ -17,13 +17,11 @@ func NewConfigRepository(db *gorm.DB) *ConfigRepository {
}
}
// UpdateConfig updates or creates a Config record
func (r *ConfigRepository) UpdateConfig(ctx context.Context, config *model.Config) *model.Config {
if err := r.Update(ctx, config); err != nil {
// If update fails, try to insert
if err := r.Insert(ctx, config); err != nil {
return nil
}
}
return config
}
}

View File

@@ -8,20 +8,16 @@ import (
"gorm.io/gorm"
)
// MembershipRepository handles database operations for users, roles, and permissions.
type MembershipRepository struct {
*BaseRepository[model.User, model.MembershipFilter]
}
// NewMembershipRepository creates a new MembershipRepository.
func NewMembershipRepository(db *gorm.DB) *MembershipRepository {
return &MembershipRepository{
BaseRepository: NewBaseRepository[model.User, model.MembershipFilter](db, model.User{}),
}
}
// FindUserByUsername finds a user by their username.
// It preloads the user's role and the role's permissions.
func (r *MembershipRepository) FindUserByUsername(ctx context.Context, username string) (*model.User, error) {
var user model.User
db := r.db.WithContext(ctx)
@@ -32,7 +28,6 @@ func (r *MembershipRepository) FindUserByUsername(ctx context.Context, username
return &user, nil
}
// FindUserByIDWithPermissions finds a user by their ID and preloads Role and Permissions.
func (r *MembershipRepository) FindUserByIDWithPermissions(ctx context.Context, userID string) (*model.User, error) {
var user model.User
db := r.db.WithContext(ctx)
@@ -43,13 +38,11 @@ func (r *MembershipRepository) FindUserByIDWithPermissions(ctx context.Context,
return &user, nil
}
// CreateUser creates a new user.
func (r *MembershipRepository) CreateUser(ctx context.Context, user *model.User) error {
db := r.db.WithContext(ctx)
return db.Create(user).Error
}
// FindRoleByName finds a role by its name.
func (r *MembershipRepository) FindRoleByName(ctx context.Context, name string) (*model.Role, error) {
var role model.Role
db := r.db.WithContext(ctx)
@@ -60,13 +53,11 @@ func (r *MembershipRepository) FindRoleByName(ctx context.Context, name string)
return &role, nil
}
// CreateRole creates a new role.
func (r *MembershipRepository) CreateRole(ctx context.Context, role *model.Role) error {
db := r.db.WithContext(ctx)
return db.Create(role).Error
}
// FindPermissionByName finds a permission by its name.
func (r *MembershipRepository) FindPermissionByName(ctx context.Context, name string) (*model.Permission, error) {
var permission model.Permission
db := r.db.WithContext(ctx)
@@ -77,19 +68,16 @@ func (r *MembershipRepository) FindPermissionByName(ctx context.Context, name st
return &permission, nil
}
// CreatePermission creates a new permission.
func (r *MembershipRepository) CreatePermission(ctx context.Context, permission *model.Permission) error {
db := r.db.WithContext(ctx)
return db.Create(permission).Error
}
// AssignPermissionsToRole assigns a set of permissions to a role.
func (r *MembershipRepository) AssignPermissionsToRole(ctx context.Context, role *model.Role, permissions []model.Permission) error {
db := r.db.WithContext(ctx)
return db.Model(role).Association("Permissions").Replace(permissions)
}
// GetUserPermissions retrieves all permissions for a given user ID.
func (r *MembershipRepository) GetUserPermissions(ctx context.Context, userID uuid.UUID) ([]string, error) {
var user model.User
db := r.db.WithContext(ctx)
@@ -106,7 +94,6 @@ func (r *MembershipRepository) GetUserPermissions(ctx context.Context, userID uu
return permissions, nil
}
// ListUsers retrieves all users.
func (r *MembershipRepository) ListUsers(ctx context.Context) ([]*model.User, error) {
var users []*model.User
db := r.db.WithContext(ctx)
@@ -114,13 +101,11 @@ func (r *MembershipRepository) ListUsers(ctx context.Context) ([]*model.User, er
return users, err
}
// DeleteUser deletes a user.
func (r *MembershipRepository) DeleteUser(ctx context.Context, userID uuid.UUID) error {
db := r.db.WithContext(ctx)
return db.Delete(&model.User{}, "id = ?", userID).Error
}
// FindUserByID finds a user by their ID.
func (r *MembershipRepository) FindUserByID(ctx context.Context, userID uuid.UUID) (*model.User, error) {
var user model.User
db := r.db.WithContext(ctx)
@@ -131,13 +116,11 @@ func (r *MembershipRepository) FindUserByID(ctx context.Context, userID uuid.UUI
return &user, nil
}
// UpdateUser updates a user's details in the database.
func (r *MembershipRepository) UpdateUser(ctx context.Context, user *model.User) error {
db := r.db.WithContext(ctx)
return db.Save(user).Error
}
// FindRoleByID finds a role by its ID.
func (r *MembershipRepository) FindRoleByID(ctx context.Context, roleID uuid.UUID) (*model.Role, error) {
var role model.Role
db := r.db.WithContext(ctx)
@@ -148,12 +131,10 @@ func (r *MembershipRepository) FindRoleByID(ctx context.Context, roleID uuid.UUI
return &role, nil
}
// ListUsersWithFilter retrieves users based on the membership filter.
func (r *MembershipRepository) ListUsersWithFilter(ctx context.Context, filter *model.MembershipFilter) (*[]model.User, error) {
return r.BaseRepository.GetAll(ctx, filter)
}
// ListRoles retrieves all roles.
func (r *MembershipRepository) ListRoles(ctx context.Context) ([]*model.Role, error) {
var roles []*model.Role
db := r.db.WithContext(ctx)

View File

@@ -10,11 +10,7 @@ import (
"go.uber.org/dig"
)
// InitializeRepositories
// Initializes Dependency Injection modules for repositories
//
// Args:
// *dig.Container: Dig Container
// *dig.Container: Dig Container
func InitializeRepositories(c *dig.Container) {
c.Provide(NewServiceControlRepository)
c.Provide(NewStateHistoryRepository)
@@ -24,16 +20,14 @@ func InitializeRepositories(c *dig.Container) {
c.Provide(NewSteamCredentialsRepository)
c.Provide(NewMembershipRepository)
// Provide the Steam2FAManager as a singleton
if err := c.Provide(func() *model.Steam2FAManager {
manager := model.NewSteam2FAManager()
// Use graceful shutdown manager for cleanup goroutine
shutdownManager := graceful.GetManager()
shutdownManager.RunGoroutine(func(ctx context.Context) {
ticker := time.NewTicker(15 * time.Minute)
defer ticker.Stop()
for {
select {
case <-ctx.Done():
@@ -43,7 +37,7 @@ func InitializeRepositories(c *dig.Container) {
}
}
})
return manager
}); err != nil {
logging.Panic("unable to initialize steam 2fa manager")

View File

@@ -20,10 +20,6 @@ func NewServerRepository(db *gorm.DB) *ServerRepository {
return repo
}
// GetFirstByServiceName
// Gets first row from Server table.
//
// Args:
// context.Context: Application context
// Returns:
// model.ServerModel: Server object from database.

View File

@@ -18,17 +18,14 @@ func NewStateHistoryRepository(db *gorm.DB) *StateHistoryRepository {
}
}
// GetAll retrieves all state history records with the given filter
func (r *StateHistoryRepository) GetAll(ctx context.Context, filter *model.StateHistoryFilter) (*[]model.StateHistory, error) {
return r.BaseRepository.GetAll(ctx, filter)
}
// Insert creates a new state history record
func (r *StateHistoryRepository) Insert(ctx context.Context, model *model.StateHistory) error {
return r.BaseRepository.Insert(ctx, model)
}
// GetLastSessionID gets the last session ID for a server
func (r *StateHistoryRepository) GetLastSessionID(ctx context.Context, serverID uuid.UUID) (uuid.UUID, error) {
var lastSession model.StateHistory
result := r.BaseRepository.db.WithContext(ctx).
@@ -38,7 +35,7 @@ func (r *StateHistoryRepository) GetLastSessionID(ctx context.Context, serverID
if result.Error != nil {
if result.Error == gorm.ErrRecordNotFound {
return uuid.Nil, nil // Return nil UUID if no sessions found
return uuid.Nil, nil
}
return uuid.Nil, result.Error
}
@@ -46,10 +43,8 @@ func (r *StateHistoryRepository) GetLastSessionID(ctx context.Context, serverID
return lastSession.SessionID, nil
}
// GetSummaryStats calculates peak players, total sessions, and average players.
func (r *StateHistoryRepository) GetSummaryStats(ctx context.Context, filter *model.StateHistoryFilter) (model.StateHistoryStats, error) {
var stats model.StateHistoryStats
// Parse ServerID to UUID for query
serverUUID, err := uuid.Parse(filter.ServerID)
if err != nil {
return model.StateHistoryStats{}, err
@@ -73,12 +68,10 @@ func (r *StateHistoryRepository) GetSummaryStats(ctx context.Context, filter *mo
return stats, nil
}
// GetTotalPlaytime calculates the total playtime in minutes.
func (r *StateHistoryRepository) GetTotalPlaytime(ctx context.Context, filter *model.StateHistoryFilter) (int, error) {
var totalPlaytime struct {
TotalMinutes float64
}
// Parse ServerID to UUID for query
serverUUID, err := uuid.Parse(filter.ServerID)
if err != nil {
return 0, err
@@ -100,10 +93,8 @@ func (r *StateHistoryRepository) GetTotalPlaytime(ctx context.Context, filter *m
return int(totalPlaytime.TotalMinutes), nil
}
// GetPlayerCountOverTime gets downsampled player count data.
func (r *StateHistoryRepository) GetPlayerCountOverTime(ctx context.Context, filter *model.StateHistoryFilter) ([]model.PlayerCountPoint, error) {
var points []model.PlayerCountPoint
// Parse ServerID to UUID for query
serverUUID, err := uuid.Parse(filter.ServerID)
if err != nil {
return points, err
@@ -122,10 +113,8 @@ func (r *StateHistoryRepository) GetPlayerCountOverTime(ctx context.Context, fil
return points, err
}
// GetSessionTypes counts sessions by type.
func (r *StateHistoryRepository) GetSessionTypes(ctx context.Context, filter *model.StateHistoryFilter) ([]model.SessionCount, error) {
var sessionTypes []model.SessionCount
// Parse ServerID to UUID for query
serverUUID, err := uuid.Parse(filter.ServerID)
if err != nil {
return sessionTypes, err
@@ -145,10 +134,8 @@ func (r *StateHistoryRepository) GetSessionTypes(ctx context.Context, filter *mo
return sessionTypes, err
}
// GetDailyActivity counts sessions per day.
func (r *StateHistoryRepository) GetDailyActivity(ctx context.Context, filter *model.StateHistoryFilter) ([]model.DailyActivity, error) {
var dailyActivity []model.DailyActivity
// Parse ServerID to UUID for query
serverUUID, err := uuid.Parse(filter.ServerID)
if err != nil {
return dailyActivity, err
@@ -167,10 +154,8 @@ func (r *StateHistoryRepository) GetDailyActivity(ctx context.Context, filter *m
return dailyActivity, err
}
// GetRecentSessions retrieves the 10 most recent sessions.
func (r *StateHistoryRepository) GetRecentSessions(ctx context.Context, filter *model.StateHistoryFilter) ([]model.RecentSession, error) {
var recentSessions []model.RecentSession
// Parse ServerID to UUID for query
serverUUID, err := uuid.Parse(filter.ServerID)
if err != nil {
return recentSessions, err