package main import ( "errors" "flag" "log" "os" "strconv" "github.com/gochan-org/gochan/cmd/gochan-migration/internal/common" "github.com/gochan-org/gochan/cmd/gochan-migration/internal/gcupdate" "github.com/gochan-org/gochan/cmd/gochan-migration/internal/pre2021" "github.com/gochan-org/gochan/pkg/config" "github.com/gochan-org/gochan/pkg/gcsql" ) var ( versionStr string migrator common.DBMigrator dbVersionStr string ) func cleanup() { var err error exitCode := 0 if migrator != nil { if err = migrator.Close(); err != nil { common.LogError().Err(err).Caller().Msg("Error closing migrator") exitCode = 1 } } if err = gcsql.Close(); err != nil { common.LogError().Err(err).Caller().Msg("Error closing SQL connection") exitCode = 1 } os.Exit(exitCode) } func main() { var options common.MigrationOptions var updateDB bool flag.BoolVar(&updateDB, "updatedb", false, "If this is set, gochan-migrate will check, and if needed, update gochan's database schema") flag.StringVar(&options.ChanType, "oldchan", "", "The imageboard we are migrating from (currently only pre2021 is supported, but more are coming") flag.StringVar(&options.OldChanConfig, "oldconfig", "", "The path to the old chan's configuration file") flag.Parse() err := config.InitConfig(versionStr) if err != nil { log.Fatalln("Unable to initialize configuration:", err.Error()) } if err = common.InitMigrationLog(); err != nil { log.Fatalln("Unable to initialize migration log:", err.Error()) } fatalEv := common.LogFatal() defer func() { cleanup() fatalEv.Discard() }() if !updateDB { if options.ChanType == "" { flag.PrintDefaults() fatalEv.Msg("Missing required oldchan value") } else if options.OldChanConfig == "" { flag.PrintDefaults() fatalEv.Msg("Missing required oldconfig value") } } else if updateDB { options.ChanType = "gcupdate" } fatalEv.Str("chanType", options.ChanType) switch options.ChanType { case "gcupdate": targetDBVer, err := strconv.Atoi(dbVersionStr) if err != nil { fatalEv.Err(err).Caller().Msg("Invalid database version string, unable to parse as integer") } migrator = &gcupdate.GCDatabaseUpdater{ TargetDBVer: targetDBVer, } case "pre2021": migrator = &pre2021.Pre2021Migrator{} case "kusabax": fallthrough case "tinyboard": fallthrough default: fatalEv.Msg("Unsupported chan type, Currently only pre2021 database migration is supported") } migratingInPlace := migrator.IsMigratingInPlace() common.LogInfo(). Str("oldChanType", options.ChanType). Str("oldChanConfig", options.OldChanConfig). Bool("migratingInPlace", migratingInPlace). Msg("Starting database migration") if err = config.InitConfig(versionStr); err != nil { fatalEv.Err(err).Caller().Msg("Unable to reload configuration") } sqlCfg := config.GetSQLConfig() if migratingInPlace && sqlCfg.DBtype == "sqlite3" && !updateDB { common.LogWarning(). Str("dbType", sqlCfg.DBtype). Bool("migrateInPlace", migratingInPlace). Msg("SQLite has limitations with table column changes") } if !migratingInPlace { err = gcsql.ConnectToDB(&sqlCfg) if err != nil { fatalEv.Err(err).Caller().Msg("Failed to connect to the database") } if err = gcsql.CheckAndInitializeDatabase(sqlCfg.DBtype, dbVersionStr); err != nil { fatalEv.Err(err).Caller().Msg("Unable to initialize the database") } } if err = migrator.Init(&options); err != nil { fatalEv.Err(err).Caller().Msg("Unable to initialize migrator") } var migrated bool migrated, err = migrator.MigrateDB() if errors.Is(err, common.ErrNotInstalled) { common.LogWarning().Msg(err.Error()) return } else if err != nil { fatalEv.Err(err).Msg("Unable to migrate database") } if migrated { common.LogWarning().Msg("Database is already migrated") } else { common.LogInfo().Str("chanType", options.ChanType).Msg("Database migration complete") } }