feat(s3): add S3 move (object & prefix) support: server handlers, API client methods, UI dialogs/picker, drag-and-drop and validation

This commit is contained in:
2026-01-28 15:35:28 +00:00
parent 4603154408
commit e379c2b6b1
11 changed files with 1064 additions and 10 deletions

View File

@@ -1 +1,2 @@
export * from './formatters.js';
export * from './move-validator.js';

View File

@@ -0,0 +1,47 @@
/**
* Move validation utilities for S3 objects
*/
export interface IMoveValidation {
valid: boolean;
error?: string;
}
/**
* Validates if a move operation is allowed
* @param sourceKey The source object key (file or folder with trailing /)
* @param destPrefix The destination prefix (folder)
* @returns Validation result with error message if invalid
*/
export function validateMove(sourceKey: string, destPrefix: string): IMoveValidation {
// Check: Moving folder into itself or a subfolder of itself
if (sourceKey.endsWith('/')) {
// It's a folder - check if destPrefix starts with sourceKey
if (destPrefix.startsWith(sourceKey)) {
return { valid: false, error: 'Cannot move a folder into itself' };
}
}
// Check: Source and dest are the same location
const sourceParent = getParentPrefix(sourceKey);
if (sourceParent === destPrefix) {
return { valid: false, error: 'Item is already in this location' };
}
return { valid: true };
}
/**
* Gets the parent prefix (directory) of a given key
* @param key The object key (file or folder)
* @returns The parent prefix
*/
export function getParentPrefix(key: string): string {
// "folder1/folder2/file.txt" -> "folder1/folder2/"
// "folder1/folder2/" -> "folder1/"
// "file.txt" -> ""
// "folder/" -> ""
const trimmed = key.endsWith('/') ? key.slice(0, -1) : key;
const lastSlash = trimmed.lastIndexOf('/');
return lastSlash >= 0 ? trimmed.substring(0, lastSlash + 1) : '';
}