Changeset 0.21.1.1 (#87)

This commit is contained in:
2026-03-01 16:40:26 +00:00
parent 70aa78e486
commit 09c7281552
20 changed files with 853 additions and 102 deletions

View File

@@ -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)