//go:build ignore package main import ( "encoding/json" "flag" "fmt" "log" "os" "sort" "time" "github.com/glebarez/sqlite" "gorm.io/gorm" ) type snapshot struct { GeneratedAt string `json:"generated_at"` Path string `json:"path"` FileSize int64 `json:"file_size"` Existing []string `json:"existing_tables"` Missing []string `json:"missing_tables"` Tables map[string]int64 `json:"tables"` SampleUsers []string `json:"sample_users"` } func main() { dbPath := flag.String("db", "./data/user_management.db", "sqlite database path") jsonOutput := flag.Bool("json", false, "emit snapshot as JSON") flag.Parse() info, err := os.Stat(*dbPath) if err != nil { log.Fatalf("stat db failed: %v", err) } db, err := gorm.Open(sqlite.Open(*dbPath), &gorm.Config{}) if err != nil { log.Fatalf("open db failed: %v", err) } tableNames := []string{ "users", "roles", "permissions", "user_roles", "role_permissions", "devices", "login_logs", "operation_logs", "social_accounts", "webhooks", "webhook_deliveries", "password_histories", } var existingTables []string if err := db.Raw("SELECT name FROM sqlite_master WHERE type = 'table'").Scan(&existingTables).Error; err != nil { log.Fatalf("load sqlite table names failed: %v", err) } sort.Strings(existingTables) existingTableSet := make(map[string]struct{}, len(existingTables)) for _, tableName := range existingTables { existingTableSet[tableName] = struct{}{} } tableCounts := make(map[string]int64, len(tableNames)) missingTables := make([]string, 0) for _, tableName := range tableNames { if _, ok := existingTableSet[tableName]; !ok { missingTables = append(missingTables, tableName) continue } var count int64 if err := db.Raw("SELECT COUNT(*) FROM " + tableName).Scan(&count).Error; err != nil { log.Fatalf("count table %s failed: %v", tableName, err) } tableCounts[tableName] = count } var sampleUsers []string if err := db.Raw("SELECT username FROM users ORDER BY id ASC LIMIT 10").Scan(&sampleUsers).Error; err != nil { log.Fatalf("load sample users failed: %v", err) } sort.Strings(sampleUsers) result := snapshot{ GeneratedAt: time.Now().Format(time.RFC3339), Path: *dbPath, FileSize: info.Size(), Existing: existingTables, Missing: missingTables, Tables: tableCounts, SampleUsers: sampleUsers, } if *jsonOutput { encoder := json.NewEncoder(os.Stdout) encoder.SetIndent("", " ") if err := encoder.Encode(result); err != nil { log.Fatalf("encode snapshot failed: %v", err) } return } fmt.Printf("snapshot generated_at=%s\n", result.GeneratedAt) fmt.Printf("path=%s size=%d\n", result.Path, result.FileSize) for _, tableName := range tableNames { if count, ok := result.Tables[tableName]; ok { fmt.Printf("%s=%d\n", tableName, count) continue } fmt.Printf("%s=missing\n", tableName) } fmt.Printf("sample_users=%v\n", result.SampleUsers) }