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:
@@ -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;
|
||||
}
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user