refactor: unify data directory structure

Unify user data directory from fragmented layout to consistent nested format:

- Move WebDAV from: data/files/<username> → data/<username>/files/
- Move CalDAV from: data/<username>/cal-<name> → data/<username>/calendars/<name>
- Move CardDAV from: data/<username>/card-<name> → data/<username>/addressbooks/<name>

Changes:
- internal/store/store.go: Update collectionPath() to map collection names
- internal/store/migrate.go: Add idempotent Migrate() method
- internal/store/migrate_test.go: Comprehensive migration tests
- internal/webdav/handler.go: Use new unified path structure
- cmd/server/main.go: Auto-run migration on startup
- tools/nidusctl/main.go: Add migrate subcommand
- Update tests to verify new structure

URL endpoints unchanged - only on-disk structure modified. All tests pass.
This commit is contained in:
2026-08-30 12:15:31 +02:00
parent f463c01f0f
commit 2fd39c8180
14 changed files with 964 additions and 22 deletions
+22 -3
View File
@@ -48,9 +48,26 @@ func (s *Store) lockFor(user string) *sync.RWMutex {
return l
}
// collectionPath returns the filesystem path for a collection.
// collectionPath returns the filesystem path for a collection in the new unified format.
// Calendar collections: <username>/calendars/<name>
// Address book collections: <username>/addressbooks/<name>
// WebDAV collections: <username>/files (all files in one directory)
func (s *Store) collectionPath(user, collection string) string {
return filepath.Join(s.rootDir, sanitize(user), sanitize(collection))
userDir := filepath.Join(s.rootDir, sanitize(user))
if strings.HasPrefix(collection, "cal-") {
return filepath.Join(userDir, "calendars", strings.TrimPrefix(collection, "cal-"))
}
if strings.HasPrefix(collection, "card-") {
return filepath.Join(userDir, "addressbooks", strings.TrimPrefix(collection, "card-"))
}
if collection == "files" {
return filepath.Join(userDir, "files")
}
return filepath.Join(userDir, sanitize(collection))
}
// objectPath returns the filesystem path for an object within a collection.
@@ -68,6 +85,7 @@ func (s *Store) EnsureCollection(user, collection string) error {
}
// ListCollections returns all collection names for a user.
// Returns both old-style (cal-*, card-*) and new-style (calendars/*, addressbooks/*) collections.
func (s *Store) ListCollections(user string) ([]string, error) {
l := s.lockFor(user)
l.RLock()
@@ -85,7 +103,8 @@ func (s *Store) ListCollections(user string) ([]string, error) {
var names []string
for _, e := range entries {
if e.IsDir() {
names = append(names, e.Name())
name := e.Name()
names = append(names, name)
}
}
return names, nil