refactor(backend): split seed tool into domain-specific modules
Extract monolithic seed main.go into separate files per domain:
users, tracks, playlists, chat, analytics, marketplace, social,
content, live, moderation, notifications, and misc. Add config,
fake data helpers, and utility modules. Update Makefile targets.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 22:35:07 +00:00
package main
import (
"database/sql"
"fmt"
"time"
)
// SeedContent creates files, user_storage_quotas, courses, lessons, gear_items, groups.
func SeedContent ( db * sql . DB , cfg Config , users [ ] SeededUser , tracks [ ] SeededTrack ) error {
fmt . Println ( "\n═══ CONTENT ═══" )
artists := GetArtists ( users )
// ── 1. Files (simulated entries for tracks and avatars) ──────────────────
p := NewProgress ( "files" , cfg . FileEntries )
fileRows := make ( [ ] [ ] interface { } , 0 , cfg . FileEntries )
// Audio files for tracks
for i , t := range tracks {
if i >= cfg . FileEntries * 2 / 3 {
break
}
fileSize := int64 ( t . Duration ) * int64 ( randInt ( 16000 , 32000 ) )
storagePath := fmt . Sprintf ( "audio/%s.mp3" , t . ID )
url := fmt . Sprintf ( "https://cdn.veza.music/%s" , storagePath )
fileRows = append ( fileRows , [ ] interface { } {
newUUID ( ) , t . CreatorID ,
fmt . Sprintf ( "%s.mp3" , t . ID ) , fmt . Sprintf ( "%s.mp3" , t . Title ) ,
"audio/mpeg" , fileSize , storagePath , "s3" ,
"veza-audio" , url , nil , // thumbnail_url
fmt . Sprintf ( "%x" , rng . Int63 ( ) ) , nil , // file_hash, metadata
true , time . Now ( ) , nil , // is_processed, processed_at, processing_error
true , "clean" , time . Now ( ) , // virus_scanned, virus_scan_result, virus_scanned_at
false , // is_public
time . Now ( ) , time . Now ( ) , nil ,
} )
}
// Avatar files for users
avatarCount := cfg . FileEntries / 3
for i := 0 ; i < avatarCount && i < len ( users ) ; i ++ {
u := users [ i ]
fileSize := int64 ( randInt ( 50000 , 500000 ) ) // 50KB - 500KB
storagePath := fmt . Sprintf ( "avatars/%s.jpg" , u . ID )
url := fmt . Sprintf ( "https://cdn.veza.music/%s" , storagePath )
fileRows = append ( fileRows , [ ] interface { } {
newUUID ( ) , u . ID ,
fmt . Sprintf ( "%s.jpg" , u . ID ) , "avatar.jpg" ,
"image/jpeg" , fileSize , storagePath , "s3" ,
"veza-avatars" , url , nil ,
fmt . Sprintf ( "%x" , rng . Int63 ( ) ) , nil ,
true , time . Now ( ) , nil ,
true , "clean" , time . Now ( ) ,
true ,
u . CreatedAt , u . CreatedAt , nil ,
} )
}
_ , err := BulkInsert ( db , "files" ,
"id, user_id, filename, original_filename, mime_type, file_size, storage_path, storage_provider, bucket_name, url, thumbnail_url, file_hash, metadata, is_processed, processed_at, processing_error, virus_scanned, virus_scan_result, virus_scanned_at, is_public, created_at, updated_at, deleted_at" ,
fileRows )
if err != nil {
return fmt . Errorf ( "insert files: %w" , err )
}
p . Update ( len ( fileRows ) )
p . Done ( )
// ── 2. User storage quotas ───────────────────────────────────────────────
p = NewProgress ( "user_storage_quotas" , len ( artists ) )
quotaRows := make ( [ ] [ ] interface { } , 0 , len ( artists ) )
for _ , a := range artists {
usedBytes := int64 ( randInt ( 100 , 5000 ) ) * 1024 * 1024 // 100MB - 5GB
maxBytes := int64 ( 10 ) * 1024 * 1024 * 1024 // 10GB
quotaRows = append ( quotaRows , [ ] interface { } {
newUUID ( ) , a . ID , usedBytes , maxBytes , time . Now ( ) , time . Now ( ) ,
} )
}
_ , _ = BulkInsert ( db , "user_storage_quotas" ,
"id, user_id, used_bytes, max_bytes, created_at, updated_at" ,
quotaRows )
p . Update ( len ( quotaRows ) )
p . Done ( )
// ── 3. Courses & Lessons ─────────────────────────────────────────────────
p = NewProgress ( "courses" , cfg . Courses )
courseRows := make ( [ ] [ ] interface { } , 0 , cfg . Courses )
type courseInfo struct {
id string
lessonCount int
}
courses := make ( [ ] courseInfo , 0 , cfg . Courses )
courseCategories := [ ] string { "production" , "mixing" , "sound-design" , "songwriting" , "djing" , "mastering" }
levels := [ ] string { "beginner" , "intermediate" , "advanced" }
lessonTitles := [ ] string {
"Getting Started" , "Setting Up Your DAW" , "Understanding Audio Basics" ,
"Your First Beat" , "Melody and Harmony" , "Sound Design Fundamentals" ,
"Arrangement Techniques" , "Mixing Basics" , "EQ and Compression" ,
"Effects and Processing" , "Mastering Your Track" , "Final Project" ,
"Advanced Techniques" , "Creative Workflows" , "Industry Standards" ,
}
for i := 0 ; i < cfg . Courses && i < len ( artists ) ; i ++ {
id := newUUID ( )
artist := artists [ i % len ( artists ) ]
title := fmt . Sprintf ( "%s Masterclass — %s" , GenreForArtist ( i ) [ 0 ] . Name , pick ( [ ] string { "From Zero to Pro" , "Complete Guide" , "Deep Dive" , "Essentials" , "Workshop" } ) )
slug := fmt . Sprintf ( "course-%d" , i )
category := pick ( courseCategories )
level := pick ( levels )
price := randInt ( 0 , 9999 ) // 0 = free
lessonCount := randInt ( 5 , 15 )
createdAt := RandomTimeAfter ( artist . CreatedAt )
var publishedAt interface { } = createdAt
status := "published"
if randChance ( 10 ) {
status = "draft"
publishedAt = nil
}
c := courseInfo { id : id , lessonCount : lessonCount }
courses = append ( courses , c )
courseRows = append ( courseRows , [ ] interface { } {
id , artist . ID , title , slug ,
fmt . Sprintf ( "Learn %s with %s. Comprehensive course covering everything from basics to advanced techniques." , category , artist . DisplayName ) ,
category , fmt . Sprintf ( "{music,production,%s}" , category ) ,
price , "EUR" , "fixed" , status , level , "fr" ,
lessonCount , publishedAt , createdAt , createdAt ,
} )
}
_ , err = BulkInsert ( db , "courses" ,
"id, creator_id, title, slug, description, category, tags, price_cents, currency, pricing_model, status, level, language, lesson_count, published_at, created_at, updated_at" ,
courseRows )
if err != nil {
return fmt . Errorf ( "insert courses: %w" , err )
}
p . Update ( len ( courseRows ) )
p . Done ( )
// Lessons
totalLessons := 0
for _ , c := range courses {
totalLessons += c . lessonCount
}
p = NewProgress ( "lessons" , totalLessons )
lessonRows := make ( [ ] [ ] interface { } , 0 , totalLessons )
for _ , c := range courses {
for li := 0 ; li < c . lessonCount ; li ++ {
title := lessonTitles [ li % len ( lessonTitles ) ]
lessonRows = append ( lessonRows , [ ] interface { } {
newUUID ( ) , c . id , li , title ,
fmt . Sprintf ( "Lesson %d: %s" , li + 1 , title ) ,
randInt ( 300 , 1800 ) , // duration 5-30min
2026-04-14 10:22:14 +00:00
li < 2 , // first 2 lessons free preview
refactor(backend): split seed tool into domain-specific modules
Extract monolithic seed main.go into separate files per domain:
users, tracks, playlists, chat, analytics, marketplace, social,
content, live, moderation, notifications, and misc. Add config,
fake data helpers, and utility modules. Update Makefile targets.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 22:35:07 +00:00
"completed" ,
} )
}
}
_ , _ = BulkInsert ( db , "lessons" ,
"id, course_id, order_index, title, description, duration_seconds, is_preview_free, transcoding_status" ,
lessonRows )
p . Update ( totalLessons )
p . Done ( )
// Course enrollments
enrollCount := cfg . Courses * 5
p = NewProgress ( "course_enrollments" , enrollCount )
enrollRows := make ( [ ] [ ] interface { } , 0 , enrollCount )
enrollSeen := make ( map [ string ] bool )
for i := 0 ; i < enrollCount * 2 && len ( enrollRows ) < enrollCount ; i ++ {
c := courses [ rng . Intn ( len ( courses ) ) ]
u := users [ rng . Intn ( len ( users ) ) ]
key := c . id + ":" + u . ID
if enrollSeen [ key ] {
continue
}
enrollSeen [ key ] = true
enrollRows = append ( enrollRows , [ ] interface { } {
newUUID ( ) , u . ID , c . id , "active" , time . Now ( ) ,
} )
}
_ , _ = BulkInsert ( db , "course_enrollments" ,
"id, user_id, course_id, status, purchased_at" ,
enrollRows )
p . Update ( len ( enrollRows ) )
p . Done ( )
// ── 4. Gear items ────────────────────────────────────────────────────────
p = NewProgress ( "gear_items" , cfg . GearItems )
gearRows := make ( [ ] [ ] interface { } , 0 , cfg . GearItems )
conditions := [ ] string { "Excellent" , "Good" , "Fair" }
for i := 0 ; i < cfg . GearItems ; i ++ {
artist := artists [ rng . Intn ( len ( artists ) ) ]
gear := pick ( gearTemplates )
purchaseDate := RandomTimeAfter ( artist . CreatedAt ) . Format ( "2006-01-02" )
gearRows = append ( gearRows , [ ] interface { } {
newUUID ( ) , artist . ID , gear . Name , gear . Category , gear . Brand , gear . Model ,
"Active" , pick ( conditions ) , gear . Price , "EUR" , purchaseDate ,
true , time . Now ( ) , time . Now ( ) ,
} )
}
_ , _ = BulkInsert ( db , "gear_items" ,
"id, user_id, name, category, brand, model, status, condition, purchase_price, currency, purchase_date, is_public, created_at, updated_at" ,
gearRows )
p . Update ( cfg . GearItems )
p . Done ( )
// ── 5. Groups ────────────────────────────────────────────────────────────
p = NewProgress ( "groups" , cfg . Groups )
groupRows := make ( [ ] [ ] interface { } , 0 , cfg . Groups )
type groupInfo struct {
id string
creatorID string
}
groups := make ( [ ] groupInfo , 0 , cfg . Groups )
groupNames := [ ] string {
"Electronic Producers" , "Hip-Hop Collective" , "Ambient Sound Lab" ,
"Jazz Fusion Circle" , "Indie Artists Network" , "Vinyl Enthusiasts" ,
"Sound Design Guild" , "Remix Community" , "Songwriters Hub" ,
"Studio Gear Talk" , "Music Theory Nerds" , "Live Performance Group" ,
"Lo-Fi Producers" , "Techno Underground" , "Acoustic Sessions" ,
}
for i := 0 ; i < cfg . Groups ; i ++ {
id := newUUID ( )
creator := users [ rng . Intn ( len ( users ) ) ]
name := groupNames [ i % len ( groupNames ) ]
if i >= len ( groupNames ) {
name = fmt . Sprintf ( "%s #%d" , pick ( groupNames ) , i )
}
createdAt := RandomTimeAfter ( creator . CreatedAt )
g := groupInfo { id : id , creatorID : creator . ID }
groups = append ( groups , g )
groupRows = append ( groupRows , [ ] interface { } {
id , name , fmt . Sprintf ( "A community for %s enthusiasts" , name ) ,
creator . ID , nil , true , 1 , createdAt , createdAt , nil ,
} )
}
_ , _ = BulkInsert ( db , "groups" ,
"id, name, description, creator_id, avatar_url, is_public, member_count, created_at, updated_at, deleted_at" ,
groupRows )
p . Update ( cfg . Groups )
p . Done ( )
// Group members
memberCount := cfg . Groups * 15
p = NewProgress ( "group_members" , memberCount )
gmRows := make ( [ ] [ ] interface { } , 0 , memberCount )
gmSeen := make ( map [ string ] bool )
for _ , g := range groups {
// Add creator
key := g . id + ":" + g . creatorID
gmSeen [ key ] = true
gmRows = append ( gmRows , [ ] interface { } {
newUUID ( ) , g . id , g . creatorID , "admin" , time . Now ( ) , time . Now ( ) ,
} )
// Add random members
count := randInt ( 5 , 30 )
for j := 0 ; j < count ; j ++ {
u := users [ rng . Intn ( len ( users ) ) ]
key = g . id + ":" + u . ID
if gmSeen [ key ] {
continue
}
gmSeen [ key ] = true
gmRows = append ( gmRows , [ ] interface { } {
newUUID ( ) , g . id , u . ID , "member" , time . Now ( ) , time . Now ( ) ,
} )
}
}
_ , _ = BulkInsert ( db , "group_members" ,
"id, group_id, user_id, role, joined_at, created_at" ,
gmRows )
// Update member counts
_ , _ = db . Exec ( "UPDATE groups SET member_count = (SELECT COUNT(*) FROM group_members WHERE group_members.group_id = groups.id)" )
p . Update ( len ( gmRows ) )
p . Done ( )
return nil
}