package database import ( "cardverse/internal/modules/cardmaster" "cardverse/internal/modules/seriessetmaster" "cardverse/internal/modules/user" "cardverse/internal/pkg/utils" "encoding/json" "log" "os" "path/filepath" "strconv" "strings" "time" "gorm.io/gorm" ) func Seed(db *gorm.DB) error { if err := seedAdminUser(db); err != nil { return err } if err := seedSeriesData(db); err != nil { return err } if err := seedSetData(db); err != nil { return err } if err := seedCardData(db); err != nil { return err } return nil } func seedAdminUser(db *gorm.DB) error { repo := user.NewRepository(db) const adminEmail = "admin@example.com" const adminPassword = "admin12345" existing, _ := repo.FindByEmail(adminEmail) if existing != nil { log.Println("seed: admin user sudah ada, skip") return nil } hashed, err := utils.HashPassword(adminPassword) if err != nil { return err } admin := &user.User{ Name: "Administrator", Email: adminEmail, Password: hashed, Role: user.RoleSuperAdmin, Status: user.StatusActive, IsEmailVerified: true, } if err := repo.Create(admin); err != nil { return err } log.Printf("seed: admin user berhasil dibuat (email: %s / password: %s)\n", adminEmail, adminPassword) return nil } // seriesJSON merepresentasikan struktur JSON dari file series.json type seriesJSON struct { Name string `json:"name"` ImageLocalPath string `json:"image_local_path"` ExpansionCount string `json:"expansion_count"` CardCount string `json:"card_count"` } func seedSeriesData(db *gorm.DB) error { var count int64 db.Model(&seriessetmaster.SeriesSetMaster{}).Where("parent_id IS NULL").Count(&count) if count > 0 { log.Println("seed: data series sudah ada, skip") return nil } file, err := os.ReadFile("public/json/series.json") if err != nil { return err } var rawSeries []seriesJSON if err := json.Unmarshal(file, &rawSeries); err != nil { return err } records := make([]seriessetmaster.SeriesSetMaster, 0, len(rawSeries)) for _, s := range rawSeries { expansionCount, _ := strconv.Atoi(s.ExpansionCount) cardCount, _ := strconv.Atoi(s.CardCount) records = append(records, seriessetmaster.SeriesSetMaster{ SeriesName: s.Name, Image: s.ImageLocalPath, ExpansionCount: expansionCount, CardCount: cardCount, }) } if err := db.Create(&records).Error; err != nil { return err } log.Printf("seed: %d data series berhasil dimasukkan\n", len(records)) return nil } // setJSON merepresentasikan struktur JSON dari file set (misal MA1.json) type setJSON struct { SetCode string `json:"set_code"` SetName string `json:"set_name"` SeriesName string `json:"series_name"` CoverImageLocalPath string `json:"cover_image_local_path"` LogoImageLocalPath string `json:"logo_image_local_path"` ReleaseDate string `json:"release_date"` TotalCards string `json:"total_cards"` Cards []json.RawMessage `json:"cards"` } const imagesBasePath = "public/images" // folder yang harus di-skip saat scan series var skipFolders = map[string]bool{ "elements": true, "series": true, } func seedSetData(db *gorm.DB) error { var count int64 db.Model(&seriessetmaster.SeriesSetMaster{}).Where("parent_id IS NOT NULL").Count(&count) if count > 0 { log.Println("seed: data set sudah ada, skip") return nil } var allSeries []seriessetmaster.SeriesSetMaster db.Where("parent_id IS NULL").Find(&allSeries) seriesMap := make(map[string]uint, len(allSeries)) for _, s := range allSeries { seriesMap[s.SeriesName] = s.ID } seriesFolders, err := os.ReadDir(imagesBasePath) if err != nil { return err } var totalSetsInserted int for _, seriesFolder := range seriesFolders { if !seriesFolder.IsDir() || skipFolders[seriesFolder.Name()] { continue } seriesPath := filepath.Join(imagesBasePath, seriesFolder.Name()) setFolders, err := os.ReadDir(seriesPath) if err != nil { log.Printf("seed: gagal baca folder %s: %v\n", seriesPath, err) continue } var setRecords []seriessetmaster.SeriesSetMaster var totalCardsInSeries int for _, setFolder := range setFolders { if !setFolder.IsDir() { continue } setPath := filepath.Join(seriesPath, setFolder.Name()) jsonFiles, err := filepath.Glob(filepath.Join(setPath, "*.json")) if err != nil || len(jsonFiles) == 0 { continue } rawFile, err := os.ReadFile(jsonFiles[0]) if err != nil { log.Printf("seed: gagal baca file %s: %v\n", jsonFiles[0], err) continue } var setData setJSON if err := json.Unmarshal(rawFile, &setData); err != nil { log.Printf("seed: gagal parse JSON %s: %v\n", jsonFiles[0], err) continue } parentID, exists := seriesMap[setData.SeriesName] if !exists { log.Printf("seed: series '%s' tidak ditemukan di DB, skip set '%s'\n", setData.SeriesName, setData.SetCode) continue } cardCount := len(setData.Cards) totalCardsInSeries += cardCount var releaseDate *time.Time if setData.ReleaseDate != "" { parsed, err := time.Parse("2006-01-02", setData.ReleaseDate) if err == nil { releaseDate = &parsed } } setRecords = append(setRecords, seriessetmaster.SeriesSetMaster{ ParentID: &parentID, SetCode: setData.SetCode, SetName: setData.SetName, SeriesName: setData.SeriesName, Image: setData.CoverImageLocalPath, Logo: setData.LogoImageLocalPath, ReleaseDate: releaseDate, TotalCards: cardCount, }) } if len(setRecords) == 0 { continue } if err := db.Create(&setRecords).Error; err != nil { return err } seriesName := setRecords[0].SeriesName if parentID, exists := seriesMap[seriesName]; exists { db.Model(&seriessetmaster.SeriesSetMaster{}). Where("id = ?", parentID). Updates(map[string]interface{}{ "expansion_count": len(setRecords), "card_count": totalCardsInSeries, }) } totalSetsInserted += len(setRecords) log.Printf("seed: %d set untuk series '%s' berhasil dimasukkan (total kartu: %d)\n", len(setRecords), seriesName, totalCardsInSeries) } log.Printf("seed: total %d data set berhasil dimasukkan\n", totalSetsInserted) return nil } // cardJSON merepresentasikan struktur card dari JSON type cardJSON struct { Name string `json:"name"` Number string `json:"number"` ImageLocalPath string `json:"image_local_path"` Stage string `json:"stage"` Element string `json:"element"` EvolvesFrom string `json:"evolves_from"` Illustrator string `json:"illustrator"` Regulation string `json:"regulation"` Rarity string `json:"rarity"` IDPriceChartingEng string `json:"id_price_charting_eng"` LinkPriceChartingEng string `json:"link_price_charting_eng"` IDPriceChartingJpn string `json:"id_price_charting_jpn"` LinkPriceChartingJpn string `json:"link_price_charting_jpn"` HP json.RawMessage `json:"hp"` Attacks json.RawMessage `json:"attacks"` Abilities json.RawMessage `json:"abilities"` Battle json.RawMessage `json:"battle"` EvolutionLine json.RawMessage `json:"evolution_line"` PokedexInfo json.RawMessage `json:"pokedex_info"` Effects json.RawMessage `json:"effects"` } const cardBatchSize = 100 func seedCardData(db *gorm.DB) error { var count int64 db.Model(&cardmaster.CardMaster{}).Count(&count) if count > 0 { log.Println("seed: data card sudah ada, skip") return nil } var allSets []seriessetmaster.SeriesSetMaster db.Where("parent_id IS NOT NULL").Find(&allSets) setMap := make(map[string]uint, len(allSets)) for _, s := range allSets { setMap[s.SetCode] = s.ID } seriesFolders, err := os.ReadDir(imagesBasePath) if err != nil { return err } var totalCardsInserted int for _, seriesFolder := range seriesFolders { if !seriesFolder.IsDir() || skipFolders[seriesFolder.Name()] { continue } seriesPath := filepath.Join(imagesBasePath, seriesFolder.Name()) setFolders, err := os.ReadDir(seriesPath) if err != nil { continue } for _, setFolder := range setFolders { if !setFolder.IsDir() { continue } setPath := filepath.Join(seriesPath, setFolder.Name()) jsonFiles, err := filepath.Glob(filepath.Join(setPath, "*.json")) if err != nil || len(jsonFiles) == 0 { continue } rawFile, err := os.ReadFile(jsonFiles[0]) if err != nil { continue } var setData setJSON if err := json.Unmarshal(rawFile, &setData); err != nil { continue } setID, exists := setMap[setData.SetCode] if !exists { continue } cardRecords := make([]cardmaster.CardMaster, 0, len(setData.Cards)) for _, rawCard := range setData.Cards { var c cardJSON if err := json.Unmarshal(rawCard, &c); err != nil { continue } enrichedHP := enrichHP(c.HP) enrichedAttacks := enrichAttacks(c.Attacks) enrichedBattle := enrichBattle(c.Battle) cardRecords = append(cardRecords, cardmaster.CardMaster{ IDSet: setID, Name: c.Name, Number: c.Number, Image: c.ImageLocalPath, Stage: c.Stage, Element: c.Element, ElementImage: cardmaster.GetElementImage(c.Element), EvolvesFrom: c.EvolvesFrom, Illustrator: c.Illustrator, Regulation: c.Regulation, Rarity: c.Rarity, IDPriceChartingEng: c.IDPriceChartingEng, LinkPriceChartingEng: c.LinkPriceChartingEng, IDPriceChartingJpn: c.IDPriceChartingJpn, LinkPriceChartingJpn: c.LinkPriceChartingJpn, HP: cardmaster.JSONB(enrichedHP), Attacks: cardmaster.JSONB(enrichedAttacks), Abilities: cardmaster.JSONB(c.Abilities), Battle: cardmaster.JSONB(enrichedBattle), EvolutionLine: cardmaster.JSONB(c.EvolutionLine), PokedexInfo: cardmaster.JSONB(c.PokedexInfo), Effects: cardmaster.JSONB(c.Effects), }) } if len(cardRecords) == 0 { continue } if err := db.CreateInBatches(&cardRecords, cardBatchSize).Error; err != nil { return err } totalCardsInserted += len(cardRecords) log.Printf("seed: %d kartu untuk set '%s' berhasil dimasukkan\n", len(cardRecords), setData.SetCode) } } log.Printf("seed: total %d data kartu berhasil dimasukkan\n", totalCardsInserted) return nil } func enrichHP(raw json.RawMessage) json.RawMessage { if len(raw) == 0 || string(raw) == "null" { return raw } var hp cardmaster.HP if err := json.Unmarshal(raw, &hp); err != nil { return raw } hp.ElementImage = cardmaster.GetElementImage(hp.Element) enriched, err := json.Marshal(hp) if err != nil { return raw } return enriched } func enrichAttacks(raw json.RawMessage) json.RawMessage { if len(raw) == 0 || string(raw) == "null" { return raw } var rawAttacks []struct { Cost []string `json:"cost"` Name string `json:"name"` Damage string `json:"damage,omitempty"` Description string `json:"description,omitempty"` } if err := json.Unmarshal(raw, &rawAttacks); err != nil { return raw } enriched := make([]cardmaster.Attack, 0, len(rawAttacks)) for _, a := range rawAttacks { costs := make([]cardmaster.AttackCost, 0, len(a.Cost)) for _, c := range a.Cost { costs = append(costs, cardmaster.AttackCost{ Element: c, Image: cardmaster.GetElementImage(c), }) } enriched = append(enriched, cardmaster.Attack{ Cost: costs, Name: a.Name, Damage: a.Damage, Description: a.Description, }) } result, err := json.Marshal(enriched) if err != nil { return raw } return result } func enrichBattle(raw json.RawMessage) json.RawMessage { if len(raw) == 0 || string(raw) == "null" { return raw } var battle cardmaster.Battle if err := json.Unmarshal(raw, &battle); err != nil { return raw } for i := range battle.Weakness { battle.Weakness[i].ElementImage = cardmaster.GetElementImage(battle.Weakness[i].Element) } for i := range battle.Resistance { battle.Resistance[i].ElementImage = cardmaster.GetElementImage(battle.Resistance[i].Element) } for i := range battle.Retreat { battle.Retreat[i].ElementImage = cardmaster.GetElementImage(battle.Retreat[i].Element) } enriched, err := json.Marshal(battle) if err != nil { return raw } return enriched } func seriesFolderName(name string) string { lower := strings.ToLower(name) lower = strings.ReplaceAll(lower, " & ", "-") lower = strings.ReplaceAll(lower, " ", "-") return lower }