Changeset 0.21.1.1 (#87)
This commit is contained in:
@@ -301,6 +301,78 @@ func (fs *FS) Mkdir(ctx context.Context, w *models.Workspace, relPath string) er
|
||||
})
|
||||
}
|
||||
|
||||
// MoveFile renames or moves a file within the workspace.
|
||||
// Creates destination parent directories as needed.
|
||||
func (fs *FS) MoveFile(ctx context.Context, w *models.Workspace, srcRel, dstRel string) error {
|
||||
srcRel = cleanPath(srcRel)
|
||||
dstRel = cleanPath(dstRel)
|
||||
if srcRel == "" || dstRel == "" {
|
||||
return fmt.Errorf("workspace: source and destination paths required")
|
||||
}
|
||||
if srcRel == dstRel {
|
||||
return nil // no-op
|
||||
}
|
||||
|
||||
srcAbs, err := fs.absPath(w, srcRel)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dstAbs, err := fs.absPath(w, dstRel)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Ensure source exists
|
||||
srcInfo, err := os.Stat(srcAbs)
|
||||
if err != nil {
|
||||
return fmt.Errorf("workspace: source not found: %s", srcRel)
|
||||
}
|
||||
|
||||
// Ensure destination parent exists
|
||||
if err := os.MkdirAll(filepath.Dir(dstAbs), 0750); err != nil {
|
||||
return fmt.Errorf("workspace: mkdir for move: %w", err)
|
||||
}
|
||||
|
||||
// Rename on disk
|
||||
if err := os.Rename(srcAbs, dstAbs); err != nil {
|
||||
return fmt.Errorf("workspace: rename %s → %s: %w", srcRel, dstRel, err)
|
||||
}
|
||||
|
||||
// Update DB index: delete old, upsert new
|
||||
if srcInfo.IsDir() {
|
||||
// For directories, delete all files under old prefix and reconcile new prefix.
|
||||
// Simple approach: delete old entries, let next reconcile pick up new ones.
|
||||
if err := fs.store.DeleteFilesByPrefix(ctx, w.ID, srcRel+"/"); err != nil {
|
||||
log.Printf("workspace move: cleanup old prefix %s: %v", srcRel, err)
|
||||
}
|
||||
fs.store.DeleteFile(ctx, w.ID, srcRel)
|
||||
// Upsert new directory entry
|
||||
fs.store.UpsertFile(ctx, &models.WorkspaceFile{
|
||||
WorkspaceID: w.ID,
|
||||
Path: dstRel,
|
||||
IsDirectory: true,
|
||||
})
|
||||
} else {
|
||||
// Read metadata from old entry if available
|
||||
oldFile, _ := fs.store.GetFile(ctx, w.ID, srcRel)
|
||||
fs.store.DeleteFile(ctx, w.ID, srcRel)
|
||||
|
||||
newFile := &models.WorkspaceFile{
|
||||
WorkspaceID: w.ID,
|
||||
Path: dstRel,
|
||||
IsDirectory: false,
|
||||
SizeBytes: srcInfo.Size(),
|
||||
ContentType: detectContentType(dstRel, dstAbs),
|
||||
}
|
||||
if oldFile != nil {
|
||||
newFile.SHA256 = oldFile.SHA256
|
||||
}
|
||||
fs.store.UpsertFile(ctx, newFile)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Stat returns file metadata without reading content.
|
||||
func (fs *FS) Stat(ctx context.Context, w *models.Workspace, relPath string) (*models.WorkspaceFile, error) {
|
||||
relPath = cleanPath(relPath)
|
||||
|
||||
Reference in New Issue
Block a user