diff --git a/bin/memos/main.go b/bin/memos/main.go index 98821c85d..47a9ca627 100644 --- a/bin/memos/main.go +++ b/bin/memos/main.go @@ -61,12 +61,6 @@ var ( } storeInstance := store.New(dbDriver, profile) - if err := storeInstance.MigrateManually(ctx); err != nil { - cancel() - log.Error("failed to migrate manually", zap.Error(err)) - return - } - s, err := server.NewServer(ctx, profile, storeInstance) if err != nil { cancel() diff --git a/store/migrator.go b/store/migrator.go deleted file mode 100644 index 3065d94b2..000000000 --- a/store/migrator.go +++ /dev/null @@ -1,276 +0,0 @@ -package store - -import ( - "context" - "database/sql" - "fmt" - "os" - "strings" - "time" - - "github.com/lithammer/shortuuid/v4" - "github.com/pkg/errors" - - "github.com/usememos/memos/internal/log" -) - -// MigrateResourceInternalPath migrates resource internal path from absolute path to relative path. -func (s *Store) MigrateResourceInternalPath(ctx context.Context) error { - normalizedDataPath := strings.ReplaceAll(s.Profile.Data, `\`, "/") - normalizedDataPath = strings.ReplaceAll(normalizedDataPath, `//`, "/") - - db := s.driver.GetDB() - checkStmt := ` - SELECT id FROM resource - WHERE - internal_path LIKE ? - OR internal_path LIKE ? - OR internal_path LIKE ? - LIMIT 1` - rows := 0 - res := db.QueryRowContext(ctx, checkStmt, fmt.Sprintf("%s%%", s.Profile.Data), fmt.Sprintf("%s%%", normalizedDataPath), "%\\%") - if err := res.Scan(&rows); err != nil { - if rows == 0 || err == sql.ErrNoRows { - log.Debug("Resource internal path migration is not required.") - return nil - } - return errors.Wrap(err, "failed to check resource internal_path") - } - - log.Info("Migrating resource internal paths. This may take a while.") - - listResourcesStmt := ` - SELECT id, internal_path FROM resource - WHERE - internal_path IS NOT '' - AND internal_path LIKE ? - OR internal_path LIKE ? - ` - resources, err := db.QueryContext(ctx, listResourcesStmt, fmt.Sprintf("%s%%", s.Profile.Data), fmt.Sprintf("%s%%", normalizedDataPath)) - if err != nil || resources.Err() != nil { - return errors.Wrap(err, "failed to list resources") - } - defer resources.Close() - - tx, err := db.BeginTx(ctx, nil) - if err != nil { - return errors.Wrap(err, "failed to start transaction") - } - defer tx.Rollback() - - updateStmt := `UPDATE resource SET internal_path = ? WHERE id = ?` - preparedResourceUpdate, err := tx.PrepareContext(ctx, updateStmt) - if err != nil { - return errors.Wrap(err, "failed to prepare update statement") - } - defer preparedResourceUpdate.Close() - - migrateStartTime := time.Now() - migratedCount := 0 - for resources.Next() { - resource := Resource{} - if err := resources.Scan(&resource.ID, &resource.InternalPath); err != nil { - return errors.Wrap(err, "failed to parse resource data") - } - - if resource.InternalPath == "" { - continue - } - - internalPath := strings.ReplaceAll(resource.InternalPath, `\`, "/") - if !strings.HasPrefix(internalPath, normalizedDataPath) { - continue - } - - internalPath = strings.TrimPrefix(internalPath, normalizedDataPath) - - for os.IsPathSeparator(internalPath[0]) { - internalPath = internalPath[1:] - } - - _, err := preparedResourceUpdate.ExecContext(ctx, internalPath, resource.ID) - if err != nil { - return errors.Wrap(err, "failed to update resource internal_path") - } - - if migratedCount%500 == 0 { - log.Info(fmt.Sprintf("[Running] Migrated %d local resource paths", migratedCount)) - } - - migratedCount++ - } - if err := tx.Commit(); err != nil { - return errors.Wrap(err, "failed to commit transaction") - } - - if migratedCount > 0 { - log.Info(fmt.Sprintf("Migrated %d local resource paths in %s", migratedCount, time.Since(migrateStartTime))) - } - return nil -} - -// MigrateResourceName migrates resource name from other format to short UUID. -func (s *Store) MigrateResourceName(ctx context.Context) error { - db := s.driver.GetDB() - - checkStmt := ` - SELECT resource_name FROM resource - WHERE - resource_name = '' - LIMIT 1` - rows := 0 - res := db.QueryRowContext(ctx, checkStmt) - if err := res.Scan(&rows); err != nil { - if rows == 0 || err == sql.ErrNoRows { - log.Debug("Resource migration to UUIDs is not required.") - return nil - } - return errors.Wrap(err, "failed to check resource.resource_name") - } - - log.Info("Migrating resource IDs to UUIDs. This may take a while.") - - listResourceStmt := "SELECT `id`, `resource_name` FROM `resource` WHERE `resource_name` = ''" - resources, err := db.QueryContext(ctx, listResourceStmt) - if err != nil || resources.Err() != nil { - return errors.Wrap(err, "failed to list resources") - } - defer resources.Close() - - tx, err := db.BeginTx(ctx, nil) - if err != nil { - return errors.Wrap(err, "failed to start transaction") - } - defer tx.Rollback() - - updateResourceStmt := "UPDATE `resource` SET `resource_name` = ? WHERE `id` = ?" - preparedResourceUpdate, err := tx.PrepareContext(ctx, updateResourceStmt) - if err != nil { - return errors.Wrap(err, "failed to prepare update statement") - } - defer preparedResourceUpdate.Close() - - migrateStartTime := time.Now() - migratedCount := 0 - for resources.Next() { - resource := Resource{} - if err := resources.Scan(&resource.ID, &resource.ResourceName); err != nil { - return errors.Wrap(err, "failed to parse resource data") - } - - if checkResourceName(resource.ResourceName) { - continue - } - - resourceName := shortuuid.New() - if _, err := preparedResourceUpdate.ExecContext(ctx, resourceName, resource.ID); err != nil { - return errors.Wrap(err, "failed to update resource") - } - - if migratedCount%500 == 0 { - log.Info(fmt.Sprintf("[Running] Migrated %d local resources IDs", migratedCount)) - } - migratedCount++ - } - if err := tx.Commit(); err != nil { - return errors.Wrap(err, "failed to commit transaction") - } - - if migratedCount > 0 { - log.Info(fmt.Sprintf("Migrated %d resource IDs to UUIDs in %s", migratedCount, time.Since(migrateStartTime))) - } - return nil -} - -// MigrateResourceName migrates memo name from other format to short UUID. -func (s *Store) MigrateMemoName(ctx context.Context) error { - db := s.driver.GetDB() - - checkStmt := ` - SELECT resource_name FROM memo - WHERE - resource_name = '' - LIMIT 1` - rows := 0 - res := db.QueryRowContext(ctx, checkStmt) - if err := res.Scan(&rows); err != nil { - if rows == 0 || err == sql.ErrNoRows { - log.Debug("Memo migration to UUIDs is not required.") - return nil - } - return errors.Wrap(err, "failed to check memo.resource_name") - } - - log.Info("Migrating memo ids to uuids. This may take a while.") - - listMemoStmt := "SELECT `id`, `resource_name` FROM `memo` WHERE `resource_name` = ''" - memos, err := db.QueryContext(ctx, listMemoStmt) - if err != nil || memos.Err() != nil { - return errors.Wrap(err, "failed to list memos") - } - defer memos.Close() - - tx, err := db.BeginTx(ctx, nil) - if err != nil { - return errors.Wrap(err, "failed to start transaction") - } - defer tx.Rollback() - - updateMemoStmt := "UPDATE `memo` SET `resource_name` = ? WHERE `id` = ?" - preparedMemoUpdate, err := tx.PrepareContext(ctx, updateMemoStmt) - if err != nil { - return errors.Wrap(err, "failed to prepare update statement") - } - defer preparedMemoUpdate.Close() - - migrateStartTime := time.Now() - migratedCount := 0 - for memos.Next() { - memo := Memo{} - if err := memos.Scan(&memo.ID, &memo.ResourceName); err != nil { - return errors.Wrap(err, "failed to parse memo data") - } - - if checkResourceName(memo.ResourceName) { - continue - } - - resourceName := shortuuid.New() - if _, err := preparedMemoUpdate.ExecContext(ctx, resourceName, memo.ID); err != nil { - return errors.Wrap(err, "failed to update memo") - } - - if migratedCount%500 == 0 { - log.Info(fmt.Sprintf("[Running] Migrated %d local resources IDs", migratedCount)) - } - migratedCount++ - } - if err := tx.Commit(); err != nil { - return errors.Wrap(err, "failed to commit transaction") - } - - if migratedCount > 0 { - log.Info(fmt.Sprintf("Migrated %d memo ids to uuids in %s", migratedCount, time.Since(migrateStartTime))) - } - return nil -} - -func checkResourceName(resourceName string) bool { - // 22 is the length of shortuuid. - if len(resourceName) != 22 { - return false - } - for _, c := range resourceName { - if c >= '0' && c <= '9' { - continue - } - if c >= 'a' && c <= 'z' { - continue - } - if c >= 'A' && c <= 'Z' { - continue - } - return false - } - return true -} diff --git a/store/store.go b/store/store.go index dd3450b87..dcaf36b67 100644 --- a/store/store.go +++ b/store/store.go @@ -25,19 +25,6 @@ func New(driver Driver, profile *profile.Profile) *Store { } } -func (s *Store) MigrateManually(ctx context.Context) error { - if err := s.MigrateResourceInternalPath(ctx); err != nil { - return err - } - if err := s.MigrateResourceName(ctx); err != nil { - return err - } - if err := s.MigrateMemoName(ctx); err != nil { - return err - } - return nil -} - func (s *Store) Vacuum(ctx context.Context) error { return s.driver.Vacuum(ctx) } diff --git a/test/store/migrator_test.go b/test/store/migrator_test.go deleted file mode 100644 index 0d71ca4fd..000000000 --- a/test/store/migrator_test.go +++ /dev/null @@ -1,58 +0,0 @@ -package teststore - -import ( - "context" - "testing" - - "github.com/lithammer/shortuuid/v4" - "github.com/stretchr/testify/require" - - "github.com/usememos/memos/store" -) - -func TestMigrateResourceInternalPath(t *testing.T) { - ctx := context.Background() - ts := NewTestingStore(ctx, t) - user, err := createTestingHostUser(ctx, ts) - require.NoError(t, err) - - testCases := []map[string]string{ - { - ts.Profile.Data + "/assets/test.jpg": "assets/test.jpg", - }, - { - ts.Profile.Data + `\assets\test.jpg`: "assets/test.jpg", - }, - { - "/unhandled/path/test.jpg": "/unhandled/path/test.jpg", - }, - { - `C:\unhandled\path\assets\test.jpg`: `C:\unhandled\path\assets\test.jpg`, - }, - } - - for _, testCase := range testCases { - for input, expectedOutput := range testCase { - resourceCreate := &store.Resource{ - ResourceName: shortuuid.New(), - CreatorID: user.ID, - InternalPath: input, - } - createdResource, err := ts.CreateResource(ctx, resourceCreate) - require.NoError(t, err) - - err = ts.MigrateResourceInternalPath(ctx) - require.NoError(t, err) - - findResource := &store.FindResource{ - ID: &createdResource.ID, - } - resource, err := ts.GetResource(ctx, findResource) - require.NoError(t, err) - - require.Equal(t, expectedOutput, resource.InternalPath) - } - } - - ts.Close() -}