146 lines
5.4 KiB
Go
146 lines
5.4 KiB
Go
package migrations
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"time"
|
|
|
|
"game-admin/internal/config"
|
|
"game-admin/internal/models"
|
|
|
|
"github.com/uptrace/bun"
|
|
"golang.org/x/crypto/bcrypt"
|
|
)
|
|
|
|
func Migrate(ctx context.Context, db *bun.DB, cfg *config.Config) error {
|
|
tables := []interface{}{
|
|
(*models.User)(nil),
|
|
(*models.Game)(nil),
|
|
(*models.Language)(nil),
|
|
(*models.Module)(nil),
|
|
(*models.GameModule)(nil),
|
|
(*models.GameVariable)(nil),
|
|
(*models.GameVariableItem)(nil),
|
|
(*models.Notification)(nil),
|
|
(*models.NotificationDescription)(nil),
|
|
(*models.NotificationVariableDef)(nil),
|
|
(*models.NotificationEntry)(nil),
|
|
(*models.NotificationEntryVariable)(nil),
|
|
(*models.Leaderboard)(nil),
|
|
(*models.LeaderboardGroup)(nil),
|
|
(*models.LeaderboardGroupMember)(nil),
|
|
(*models.LeaderboardScore)(nil),
|
|
(*models.UserGame)(nil),
|
|
(*models.InviteLink)(nil),
|
|
(*models.BalanceTransaction)(nil),
|
|
(*models.Transaction)(nil),
|
|
}
|
|
|
|
for _, model := range tables {
|
|
_, err := db.NewCreateTable().
|
|
Model(model).
|
|
IfNotExists().
|
|
Exec(ctx)
|
|
if err != nil {
|
|
return fmt.Errorf("create table: %w", err)
|
|
}
|
|
}
|
|
|
|
_, _ = db.ExecContext(ctx,
|
|
"CREATE UNIQUE INDEX IF NOT EXISTS idx_user_games_unique ON user_games (user_id, game_id)")
|
|
_, _ = db.ExecContext(ctx,
|
|
`CREATE UNIQUE INDEX IF NOT EXISTS idx_languages_code_unique ON languages ("code")`)
|
|
_, _ = db.ExecContext(ctx,
|
|
`CREATE UNIQUE INDEX IF NOT EXISTS idx_modules_key_unique ON modules ("key")`)
|
|
_, _ = db.ExecContext(ctx,
|
|
"CREATE UNIQUE INDEX IF NOT EXISTS idx_game_modules_unique ON game_modules (game_id, module_id)")
|
|
_, _ = db.ExecContext(ctx,
|
|
`CREATE UNIQUE INDEX IF NOT EXISTS idx_game_variables_unique ON game_variables (game_id, "key")`)
|
|
_, _ = db.ExecContext(ctx,
|
|
"CREATE UNIQUE INDEX IF NOT EXISTS idx_game_variable_items_unique ON game_variable_items (game_variable_id, item_key)")
|
|
_, _ = db.ExecContext(ctx,
|
|
"CREATE UNIQUE INDEX IF NOT EXISTS idx_notifications_unique ON notifications (game_id, name)")
|
|
_, _ = db.ExecContext(ctx,
|
|
"CREATE UNIQUE INDEX IF NOT EXISTS idx_notification_descriptions_unique ON notification_descriptions (notification_id, language_id)")
|
|
_, _ = db.ExecContext(ctx,
|
|
`CREATE UNIQUE INDEX IF NOT EXISTS idx_notification_variable_defs_unique ON notification_variable_defs (notification_id, "key")`)
|
|
_, _ = db.ExecContext(ctx,
|
|
"CREATE UNIQUE INDEX IF NOT EXISTS idx_notification_entry_variables_unique ON notification_entry_variables (notification_entry_id, notification_variable_id)")
|
|
_, _ = db.ExecContext(ctx,
|
|
`CREATE UNIQUE INDEX IF NOT EXISTS idx_leaderboards_unique ON leaderboards (game_id, "key")`)
|
|
_, _ = db.ExecContext(ctx,
|
|
`CREATE UNIQUE INDEX IF NOT EXISTS idx_leaderboard_groups_unique ON leaderboard_groups (leaderboard_id, "key")`)
|
|
_, _ = db.ExecContext(ctx,
|
|
"CREATE UNIQUE INDEX IF NOT EXISTS idx_leaderboard_group_members_unique ON leaderboard_group_members (group_id, user_id)")
|
|
_, _ = db.ExecContext(ctx,
|
|
"CREATE UNIQUE INDEX IF NOT EXISTS idx_leaderboard_scores_unique ON leaderboard_scores (leaderboard_id, user_game_id)")
|
|
_, _ = db.ExecContext(ctx,
|
|
"CREATE UNIQUE INDEX IF NOT EXISTS idx_transactions_request_id_unique ON transactions (request_id)")
|
|
_, _ = db.ExecContext(ctx,
|
|
"CREATE INDEX IF NOT EXISTS idx_transactions_user_game_status ON transactions (user_id, game_id, status)")
|
|
|
|
// Seed a default admin only when there are no users yet.
|
|
userCount, err := db.NewSelect().Model((*models.User)(nil)).Count(ctx)
|
|
if err != nil {
|
|
return fmt.Errorf("count users: %w", err)
|
|
}
|
|
if userCount == 0 {
|
|
hashed, err := bcrypt.GenerateFromPassword([]byte(cfg.DefaultAdminPassword), bcrypt.DefaultCost)
|
|
if err != nil {
|
|
return fmt.Errorf("hash default admin password: %w", err)
|
|
}
|
|
|
|
admin := &models.User{
|
|
Email: cfg.DefaultAdminEmail,
|
|
Username: cfg.DefaultAdminUsername,
|
|
Password: string(hashed),
|
|
Role: models.RoleAdmin,
|
|
IsActive: true,
|
|
CreatedAt: time.Now(),
|
|
UpdatedAt: time.Now(),
|
|
}
|
|
_, err = db.NewInsert().Model(admin).Exec(ctx)
|
|
if err != nil {
|
|
return fmt.Errorf("seed default admin: %w", err)
|
|
}
|
|
|
|
fmt.Printf("default admin user seeded: %s / %s\n", cfg.DefaultAdminEmail, cfg.DefaultAdminPassword)
|
|
}
|
|
|
|
games := []models.Game{
|
|
{Name: "Black & White", Slug: "b&d", Description: "Sandbox", IsActive: true, CreatedAt: time.Now(), UpdatedAt: time.Now()},
|
|
}
|
|
for _, g := range games {
|
|
exists, _ := db.NewSelect().Model((*models.Game)(nil)).Where("slug = ?", g.Slug).Exists(ctx)
|
|
if !exists {
|
|
_, _ = db.NewInsert().Model(&g).Exec(ctx)
|
|
}
|
|
}
|
|
|
|
defaultModules := []models.Module{
|
|
{Key: "variables", Name: "Variables", CreatedAt: time.Now(), UpdatedAt: time.Now()},
|
|
{Key: "notification", Name: "Notification", CreatedAt: time.Now(), UpdatedAt: time.Now()},
|
|
{Key: "leaderboard", Name: "Leaderboard", CreatedAt: time.Now(), UpdatedAt: time.Now()},
|
|
}
|
|
for _, module := range defaultModules {
|
|
exists, _ := db.NewSelect().Model((*models.Module)(nil)).Where(`"key" = ?`, module.Key).Exists(ctx)
|
|
if !exists {
|
|
_, _ = db.NewInsert().Model(&module).Exec(ctx)
|
|
}
|
|
}
|
|
|
|
defaultLanguages := []models.Language{
|
|
{Code: "en", Name: "English", CreatedAt: time.Now(), UpdatedAt: time.Now()},
|
|
{Code: "ru", Name: "Русский", CreatedAt: time.Now(), UpdatedAt: time.Now()},
|
|
}
|
|
for _, language := range defaultLanguages {
|
|
exists, _ := db.NewSelect().Model((*models.Language)(nil)).Where("code = ?", language.Code).Exists(ctx)
|
|
if !exists {
|
|
_, _ = db.NewInsert().Model(&language).Exec(ctx)
|
|
}
|
|
}
|
|
|
|
fmt.Println("migrations complete")
|
|
return nil
|
|
}
|