Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 4520739c3a | |||
| ddac7ea41a | |||
| 75a4e6278c |
+25
-6
@@ -573,9 +573,10 @@ window.handleContextMenu = async (action) => {
|
||||
const item = selectedItems[0];
|
||||
const newName = prompt(`Enter new name for ${item.name}:`, item.name);
|
||||
if (newName && newName !== item.name) {
|
||||
const pathParts = item.path.split('/');
|
||||
const sep = item.path.includes('\\') ? '\\' : '/';
|
||||
const pathParts = item.path.split(/[/\\]/);
|
||||
pathParts[pathParts.length - 1] = newName;
|
||||
const newPath = pathParts.join('/');
|
||||
const newPath = pathParts.join(sep);
|
||||
|
||||
await Rename(currentConn, item.path, newPath);
|
||||
refreshCurrentDir();
|
||||
@@ -595,9 +596,18 @@ window.handleContextMenu = async (action) => {
|
||||
|
||||
window.navigateUp = () => {
|
||||
if (!currentPath || currentPath === '/') return;
|
||||
const parts = currentPath.split('/').filter(p => p);
|
||||
const startsWithSlash = currentPath.startsWith('/');
|
||||
const sep = currentPath.includes('\\') ? '\\' : '/';
|
||||
const parts = currentPath.split(/[/\\]/).filter(p => p);
|
||||
parts.pop();
|
||||
currentPath = parts.length ? '/' + parts.join('/') : '';
|
||||
if (parts.length === 0) {
|
||||
currentPath = '';
|
||||
} else {
|
||||
currentPath = (startsWithSlash ? '/' : '') + parts.join(sep);
|
||||
if (!startsWithSlash && parts.length === 1 && parts[0].endsWith(':')) {
|
||||
currentPath += sep;
|
||||
}
|
||||
}
|
||||
loadDirectory(currentConn, currentPath);
|
||||
};
|
||||
|
||||
@@ -641,9 +651,18 @@ window.loadTransferDestDirectory = async (connId, path) => {
|
||||
tr.innerHTML = `<td>📁 ..</td>`;
|
||||
tr.style.cursor = 'pointer';
|
||||
tr.ondblclick = () => {
|
||||
const parts = path.split('/').filter(p => p);
|
||||
const startsWithSlash = path.startsWith('/');
|
||||
const sep = path.includes('\\') ? '\\' : '/';
|
||||
const parts = path.split(/[/\\]/).filter(p => p);
|
||||
parts.pop();
|
||||
loadTransferDestDirectory(connId, parts.length ? '/' + parts.join('/') : '');
|
||||
let newPath = '';
|
||||
if (parts.length > 0) {
|
||||
newPath = (startsWithSlash ? '/' : '') + parts.join(sep);
|
||||
if (!startsWithSlash && parts.length === 1 && parts[0].endsWith(':')) {
|
||||
newPath += sep;
|
||||
}
|
||||
}
|
||||
loadTransferDestDirectory(connId, newPath);
|
||||
};
|
||||
tbody.appendChild(tr);
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
"github.com/aws/aws-sdk-go-v2/config"
|
||||
"github.com/aws/aws-sdk-go-v2/credentials"
|
||||
"github.com/aws/aws-sdk-go-v2/service/s3"
|
||||
"github.com/aws/aws-sdk-go-v2/service/s3/types"
|
||||
appcfg "goexplore/internal/config"
|
||||
"goexplore/internal/explorer"
|
||||
)
|
||||
@@ -243,13 +244,63 @@ func (e *S3Explorer) Delete(path string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// Try to delete the exact object (might be a file or a folder marker)
|
||||
_, err = e.client.DeleteObject(context.TODO(), &s3.DeleteObjectInput{
|
||||
Bucket: aws.String(bucket),
|
||||
Key: aws.String(subpath),
|
||||
})
|
||||
if err != nil {
|
||||
// Ignore error; it might just be a prefix with no exact marker
|
||||
}
|
||||
|
||||
// Also treat subpath as a folder prefix and delete all matching objects
|
||||
prefix := subpath
|
||||
if !strings.HasSuffix(prefix, "/") {
|
||||
prefix += "/"
|
||||
}
|
||||
|
||||
var continuationToken *string
|
||||
for {
|
||||
listInput := &s3.ListObjectsV2Input{
|
||||
Bucket: aws.String(bucket),
|
||||
Prefix: aws.String(prefix),
|
||||
ContinuationToken: continuationToken,
|
||||
}
|
||||
listOutput, err := e.client.ListObjectsV2(context.TODO(), listInput)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(listOutput.Contents) > 0 {
|
||||
var objectsToDelete []types.ObjectIdentifier
|
||||
for _, obj := range listOutput.Contents {
|
||||
objectsToDelete = append(objectsToDelete, types.ObjectIdentifier{
|
||||
Key: obj.Key,
|
||||
})
|
||||
}
|
||||
|
||||
_, err = e.client.DeleteObjects(context.TODO(), &s3.DeleteObjectsInput{
|
||||
Bucket: aws.String(bucket),
|
||||
Delete: &types.Delete{
|
||||
Objects: objectsToDelete,
|
||||
Quiet: aws.Bool(true),
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if listOutput.IsTruncated != nil && *listOutput.IsTruncated {
|
||||
continuationToken = listOutput.NextContinuationToken
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *S3Explorer) Rename(src, dst string) error {
|
||||
bucket1, subpath1, _, err := e.getBucketAndPath(src)
|
||||
if err != nil {
|
||||
|
||||
Reference in New Issue
Block a user