diff --git a/database/db.go b/database/db.go index 6de81d79..6b579dd9 100644 --- a/database/db.go +++ b/database/db.go @@ -4,6 +4,7 @@ package database import ( "bytes" + "errors" "io" "io/fs" "log" @@ -199,3 +200,29 @@ func Checkpoint() error { } return nil } + +// ValidateSQLiteDB opens the provided sqlite DB path with a throw-away connection +// and runs a PRAGMA integrity_check to ensure the file is structurally sound. +// It does not mutate global state or run migrations. +func ValidateSQLiteDB(dbPath string) error { + if _, err := os.Stat(dbPath); err != nil { // file must exist + return err + } + gdb, err := gorm.Open(sqlite.Open(dbPath), &gorm.Config{Logger: logger.Discard}) + if err != nil { + return err + } + sqlDB, err := gdb.DB() + if err != nil { + return err + } + defer sqlDB.Close() + var res string + if err := gdb.Raw("PRAGMA integrity_check;").Scan(&res).Error; err != nil { + return err + } + if res != "ok" { + return errors.New("sqlite integrity check failed: " + res) + } + return nil +} diff --git a/web/service/server.go b/web/service/server.go index eb261c88..b7cfc3a7 100644 --- a/web/service/server.go +++ b/web/service/server.go @@ -942,13 +942,26 @@ func (s *ServerService) ImportDB(file multipart.File) error { return common.NewErrorf("Error saving db: %v", err) } - // Check if we can init the db or not - if err = database.InitDB(tempPath); err != nil { - return common.NewErrorf("Error checking db: %v", err) + // Close temp file before opening via sqlite + if err = tempFile.Close(); err != nil { + return common.NewErrorf("Error closing temporary db file: %v", err) + } + tempFile = nil + + // Validate integrity (no migrations / side effects) + if err = database.ValidateSQLiteDB(tempPath); err != nil { + return common.NewErrorf("Invalid or corrupt db file: %v", err) } - // Stop Xray - s.StopXrayService() + // Stop Xray (ignore error but log) + if errStop := s.StopXrayService(); errStop != nil { + logger.Warningf("Failed to stop Xray before DB import: %v", errStop) + } + + // Close existing DB to release file locks (especially on Windows) + if errClose := database.CloseDB(); errClose != nil { + logger.Warningf("Failed to close existing DB before replacement: %v", errClose) + } // Backup the current database for fallback fallbackPath := fmt.Sprintf("%s.backup", config.GetDBPath()) @@ -983,7 +996,7 @@ func (s *ServerService) ImportDB(file multipart.File) error { return common.NewErrorf("Error moving db file: %v", err) } - // Migrate DB + // Open & migrate new DB if err = database.InitDB(config.GetDBPath()); err != nil { if errRename := os.Rename(fallbackPath, config.GetDBPath()); errRename != nil { return common.NewErrorf("Error migrating db and restoring fallback: %v", errRename)