feat(s3,web-ui): add S3 deletePrefix and getObjectUrl endpoints and add context menus in UI for S3 and Mongo views

This commit is contained in:
2026-01-25 11:24:03 +00:00
parent 31e9b29e23
commit 5d533caccb
12 changed files with 474 additions and 84 deletions

View File

@@ -364,4 +364,103 @@ export async function registerS3Handlers(
}
)
);
// Delete prefix (folder and all contents)
typedrouter.addTypedHandler(
new plugins.typedrequest.TypedHandler<interfaces.IReq_DeletePrefix>(
'deletePrefix',
async (reqData) => {
const smartbucket = await tsview.getSmartBucket();
if (!smartbucket) {
return { success: false };
}
try {
const bucket = await smartbucket.getBucketByName(reqData.bucketName);
if (!bucket) {
return { success: false };
}
const baseDir = await bucket.getBaseDirectory();
let targetDir = baseDir;
// Navigate to the prefix directory
const prefix = reqData.prefix.replace(/\/$/, '');
const prefixParts = prefix.split('/').filter(Boolean);
for (const part of prefixParts) {
const subDir = await targetDir.getSubDirectoryByName(part, { getEmptyDirectory: true });
if (subDir) {
targetDir = subDir;
} else {
return { success: false };
}
}
// Delete the directory and all its contents
await targetDir.delete({ mode: 'permanent' });
return { success: true };
} catch (err) {
console.error('Error deleting prefix:', err);
return { success: false };
}
}
)
);
// Get object URL (for downloads)
typedrouter.addTypedHandler(
new plugins.typedrequest.TypedHandler<interfaces.IReq_GetObjectUrl>(
'getObjectUrl',
async (reqData) => {
const smartbucket = await tsview.getSmartBucket();
if (!smartbucket) {
throw new Error('S3 not configured');
}
try {
const bucket = await smartbucket.getBucketByName(reqData.bucketName);
if (!bucket) {
throw new Error(`Bucket ${reqData.bucketName} not found`);
}
// Get the content and create a data URL
const content = await bucket.fastGet({ path: reqData.key });
const ext = reqData.key.split('.').pop()?.toLowerCase() || '';
const contentTypeMap: Record<string, string> = {
'json': 'application/json',
'txt': 'text/plain',
'html': 'text/html',
'css': 'text/css',
'js': 'application/javascript',
'ts': 'text/plain',
'tsx': 'text/plain',
'jsx': 'text/plain',
'md': 'text/markdown',
'csv': 'text/csv',
'yaml': 'text/yaml',
'yml': 'text/yaml',
'log': 'text/plain',
'sh': 'text/plain',
'env': 'text/plain',
'png': 'image/png',
'jpg': 'image/jpeg',
'jpeg': 'image/jpeg',
'gif': 'image/gif',
'webp': 'image/webp',
'svg': 'image/svg+xml',
'pdf': 'application/pdf',
'xml': 'application/xml',
};
const contentType = contentTypeMap[ext] || 'application/octet-stream';
const base64 = content.toString('base64');
const url = `data:${contentType};base64,${base64}`;
return { url };
} catch (err) {
console.error('Error getting object URL:', err);
throw err;
}
}
)
);
}