package transfer import ( "bamort/config" "bamort/database" "bamort/models" "bamort/user" "encoding/json" "fmt" "os" "path/filepath" "time" "gorm.io/gorm" ) // DatabaseExport contains all database tables for export/import type DatabaseExport struct { Version string `json:"version"` Timestamp time.Time `json:"timestamp"` // User data Users []user.User `json:"users"` // Character data Characters []models.Char `json:"characters"` Eigenschaften []models.Eigenschaft `json:"eigenschaften"` Lps []models.Lp `json:"lps"` Aps []models.Ap `json:"aps"` Bs []models.B `json:"bs"` Merkmale []models.Merkmale `json:"merkmale"` Erfahrungsschatze []models.Erfahrungsschatz `json:"erfahrungsschatze"` Bennies []models.Bennies `json:"bennies"` Vermoegen []models.Vermoegen `json:"vermoegen"` CharacterCreationSessions []models.CharacterCreationSession `json:"character_creation_sessions"` // Skills SkFertigkeiten []models.SkFertigkeit `json:"sk_fertigkeiten"` SkWaffenfertigkeiten []models.SkWaffenfertigkeit `json:"sk_waffenfertigkeiten"` SkZauber []models.SkZauber `json:"sk_zauber"` // Equipment EqAusruestungen []models.EqAusruestung `json:"eq_ausruestungen"` EqWaffen []models.EqWaffe `json:"eq_waffen"` EqContainers []models.EqContainer `json:"eq_containers"` // GSMaster data GsmSkills []models.Skill `json:"gsm_skills"` GsmWeaponSkills []models.WeaponSkill `json:"gsm_weapon_skills"` GsmSpells []models.Spell `json:"gsm_spells"` GsmEquipment []models.Equipment `json:"gsm_equipment"` GsmWeapons []models.Weapon `json:"gsm_weapons"` GsmContainers []models.Container `json:"gsm_containers"` GsmTransportations []models.Transportation `json:"gsm_transportations"` GsmBelieves []models.Believe `json:"gsm_believes"` Sources []models.Source `json:"gsm_lit_sources"` CharacterClasses []models.CharacterClass `json:"gsm_character_classes"` // Learning data SkillCategories []models.SkillCategory `json:"learning_skill_categories"` SkillDifficulties []models.SkillDifficulty `json:"learning_skill_difficulties"` SpellSchools []models.SpellSchool `json:"learning_spell_schools"` ClassCategoryEPCosts []models.ClassCategoryEPCost `json:"learning_class_category_ep_costs"` ClassSpellSchoolEPCosts []models.ClassSpellSchoolEPCost `json:"learning_class_spell_school_ep_costs"` SpellLevelLECosts []models.SpellLevelLECost `json:"learning_spell_level_le_costs"` SkillCategoryDifficulties []models.SkillCategoryDifficulty `json:"learning_skill_category_difficulties"` SkillImprovementCosts []models.SkillImprovementCost `json:"learning_skill_improvement_costs"` AuditLogEntries []models.AuditLogEntry `json:"audit_log_entries"` } // ExportResult contains information about the export operation type ExportResult struct { Filename string `json:"filename"` FilePath string `json:"filepath"` RecordCount int `json:"record_count"` Timestamp string `json:"timestamp"` } // ImportResult contains information about the import operation type ImportResult struct { RecordCount int `json:"record_count"` Timestamp string `json:"timestamp"` } // ExportDatabase exports all database content to a JSON file func ExportDatabase(exportDir string) (*ExportResult, error) { // Create export directory if it doesn't exist if err := os.MkdirAll(exportDir, 0755); err != nil { return nil, fmt.Errorf("failed to create export directory: %w", err) } export := DatabaseExport{ Version: config.GetVersion(), Timestamp: time.Now(), } // Export all tables if err := database.DB.Find(&export.Users).Error; err != nil { return nil, fmt.Errorf("failed to export users: %w", err) } if err := database.DB.Find(&export.Characters).Error; err != nil { return nil, fmt.Errorf("failed to export characters: %w", err) } database.DB.Find(&export.Eigenschaften) database.DB.Find(&export.Lps) database.DB.Find(&export.Aps) database.DB.Find(&export.Bs) database.DB.Find(&export.Merkmale) database.DB.Find(&export.Erfahrungsschatze) database.DB.Find(&export.Bennies) database.DB.Find(&export.Vermoegen) database.DB.Find(&export.CharacterCreationSessions) database.DB.Find(&export.SkFertigkeiten) database.DB.Find(&export.SkWaffenfertigkeiten) database.DB.Find(&export.SkZauber) database.DB.Find(&export.EqAusruestungen) database.DB.Find(&export.EqWaffen) database.DB.Find(&export.EqContainers) database.DB.Find(&export.GsmSkills) database.DB.Find(&export.GsmWeaponSkills) database.DB.Find(&export.GsmSpells) database.DB.Find(&export.GsmEquipment) database.DB.Find(&export.GsmWeapons) database.DB.Find(&export.GsmContainers) database.DB.Find(&export.GsmTransportations) database.DB.Find(&export.GsmBelieves) database.DB.Find(&export.Sources) database.DB.Find(&export.CharacterClasses) database.DB.Find(&export.SkillCategories) database.DB.Find(&export.SkillDifficulties) database.DB.Find(&export.SpellSchools) database.DB.Find(&export.ClassCategoryEPCosts) database.DB.Find(&export.ClassSpellSchoolEPCosts) database.DB.Find(&export.SpellLevelLECosts) database.DB.Find(&export.SkillCategoryDifficulties) database.DB.Find(&export.SkillImprovementCosts) database.DB.Find(&export.AuditLogEntries) // Count total records recordCount := len(export.Users) + len(export.Characters) + len(export.Eigenschaften) + len(export.Lps) + len(export.Aps) + len(export.Bs) + len(export.Merkmale) + len(export.Erfahrungsschatze) + len(export.Bennies) + len(export.Vermoegen) + len(export.SkFertigkeiten) + len(export.SkWaffenfertigkeiten) + len(export.SkZauber) + len(export.EqAusruestungen) + len(export.EqWaffen) + len(export.EqContainers) + len(export.GsmSkills) + len(export.GsmWeaponSkills) + len(export.GsmSpells) + len(export.GsmEquipment) + len(export.GsmWeapons) + len(export.GsmContainers) + len(export.GsmTransportations) + len(export.GsmBelieves) + len(export.Sources) + len(export.CharacterClasses) + len(export.SkillCategories) + len(export.SkillDifficulties) + len(export.SpellSchools) + len(export.ClassCategoryEPCosts) + len(export.ClassSpellSchoolEPCosts) + len(export.SpellLevelLECosts) + len(export.SkillCategoryDifficulties) + len(export.SkillImprovementCosts) + len(export.AuditLogEntries) + len(export.CharacterCreationSessions) // Generate filename with timestamp filename := fmt.Sprintf("database_export_%s.json", time.Now().Format("20060102_150405")) filepath := filepath.Join(exportDir, filename) // Marshal to JSON data, err := json.MarshalIndent(export, "", " ") if err != nil { return nil, fmt.Errorf("failed to marshal export data: %w", err) } // Write to file if err := os.WriteFile(filepath, data, 0644); err != nil { return nil, fmt.Errorf("failed to write export file: %w", err) } return &ExportResult{ Filename: filename, FilePath: filepath, RecordCount: recordCount, Timestamp: time.Now().Format(time.RFC3339), }, nil } // ImportDatabase imports all database content from a JSON file func ImportDatabase(filePath string) (*ImportResult, error) { // Read file data, err := os.ReadFile(filePath) if err != nil { return nil, fmt.Errorf("failed to read import file: %w", err) } // Unmarshal JSON var export DatabaseExport if err := json.Unmarshal(data, &export); err != nil { return nil, fmt.Errorf("failed to unmarshal import data: %w", err) } // Import all tables in transaction err = database.DB.Transaction(func(tx *gorm.DB) error { // Import users (upsert to handle existing IDs) for _, item := range export.Users { if err := tx.Save(&item).Error; err != nil { return fmt.Errorf("failed to import user: %w", err) } } // Import characters (upsert) for _, item := range export.Characters { if err := tx.Save(&item).Error; err != nil { return fmt.Errorf("failed to import character: %w", err) } } // Import character-related data (upsert) for _, item := range export.Eigenschaften { tx.Save(&item) } for _, item := range export.Lps { tx.Save(&item) } for _, item := range export.Aps { tx.Save(&item) } for _, item := range export.Bs { tx.Save(&item) } for _, item := range export.Merkmale { tx.Save(&item) } for _, item := range export.Erfahrungsschatze { tx.Save(&item) } for _, item := range export.Bennies { tx.Save(&item) } for _, item := range export.Vermoegen { tx.Save(&item) } for _, item := range export.CharacterCreationSessions { tx.Save(&item) } // Import skills for _, item := range export.SkFertigkeiten { tx.Save(&item) } for _, item := range export.SkWaffenfertigkeiten { tx.Save(&item) } for _, item := range export.SkZauber { tx.Save(&item) } // Import equipment for _, item := range export.EqAusruestungen { tx.Save(&item) } for _, item := range export.EqWaffen { tx.Save(&item) } for _, item := range export.EqContainers { tx.Save(&item) } // Import GSMaster data for _, item := range export.GsmSkills { tx.Save(&item) } for _, item := range export.GsmWeaponSkills { tx.Save(&item) } for _, item := range export.GsmSpells { tx.Save(&item) } for _, item := range export.GsmEquipment { tx.Save(&item) } for _, item := range export.GsmWeapons { tx.Save(&item) } for _, item := range export.GsmContainers { tx.Save(&item) } for _, item := range export.GsmTransportations { tx.Save(&item) } for _, item := range export.GsmBelieves { tx.Save(&item) } // Import learning data for _, item := range export.Sources { tx.Save(&item) } for _, item := range export.CharacterClasses { tx.Save(&item) } for _, item := range export.SkillCategories { tx.Save(&item) } for _, item := range export.SkillDifficulties { tx.Save(&item) } for _, item := range export.SpellSchools { tx.Save(&item) } for _, item := range export.ClassCategoryEPCosts { tx.Save(&item) } for _, item := range export.ClassSpellSchoolEPCosts { tx.Save(&item) } for _, item := range export.SpellLevelLECosts { tx.Save(&item) } for _, item := range export.SkillCategoryDifficulties { tx.Save(&item) } for _, item := range export.SkillImprovementCosts { tx.Save(&item) } for _, item := range export.AuditLogEntries { tx.Save(&item) } return nil }) if err != nil { return nil, err } recordCount := len(export.Users) + len(export.Characters) + len(export.Eigenschaften) + len(export.Lps) + len(export.Aps) + len(export.Bs) + len(export.Merkmale) + len(export.Erfahrungsschatze) + len(export.Bennies) + len(export.Vermoegen) + len(export.SkFertigkeiten) + len(export.SkWaffenfertigkeiten) + len(export.SkZauber) + len(export.EqAusruestungen) + len(export.EqWaffen) + len(export.EqContainers) + len(export.GsmSkills) + len(export.GsmWeaponSkills) + len(export.GsmSpells) + len(export.GsmEquipment) + len(export.GsmWeapons) + len(export.GsmContainers) + len(export.GsmTransportations) + len(export.GsmBelieves) + len(export.Sources) + len(export.CharacterClasses) + len(export.SkillCategories) + len(export.SkillDifficulties) + len(export.SpellSchools) + len(export.ClassCategoryEPCosts) + len(export.ClassSpellSchoolEPCosts) + len(export.SpellLevelLECosts) + len(export.SkillCategoryDifficulties) + len(export.SkillImprovementCosts) + len(export.AuditLogEntries) + len(export.CharacterCreationSessions) return &ImportResult{ RecordCount: recordCount, Timestamp: time.Now().Format(time.RFC3339), }, nil }