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

@@ -3,6 +3,7 @@ import { apiService } from '../services/index.js';
import { themeStyles } from '../styles/index.js';
const { html, css, cssManager, customElement, state, DeesElement } = plugins;
const { DeesContextmenu } = plugins.deesCatalog;
type TViewMode = 's3' | 'mongo' | 'settings';
@@ -359,30 +360,6 @@ export class TsviewApp extends DeesElement {
text-overflow: ellipsis;
white-space: nowrap;
}
.delete-btn {
opacity: 0;
padding: 4px;
background: transparent;
border: none;
color: #888;
cursor: pointer;
border-radius: 4px;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.15s;
}
.sidebar-item:hover .delete-btn,
.db-group-header:hover .delete-btn {
opacity: 1;
}
.delete-btn:hover {
background: rgba(239, 68, 68, 0.2);
color: #f87171;
}
`,
];
@@ -495,6 +472,66 @@ export class TsviewApp extends DeesElement {
}
}
private handleBucketContextMenu(event: MouseEvent, bucket: string) {
event.preventDefault();
DeesContextmenu.openContextMenuWithOptions(event, [
{
name: 'View Contents',
iconName: 'lucide:folderOpen',
action: async () => {
this.selectBucket(bucket);
},
},
{ divider: true },
{
name: 'Delete Bucket',
iconName: 'lucide:trash2',
action: async () => {
if (confirm(`Delete bucket "${bucket}"? This will delete all objects in the bucket.`)) {
const success = await apiService.deleteBucket(bucket);
if (success) {
this.buckets = this.buckets.filter(b => b !== bucket);
if (this.selectedBucket === bucket) {
this.selectedBucket = this.buckets[0] || '';
}
}
}
},
},
]);
}
private handleDatabaseContextMenu(event: MouseEvent, dbName: string) {
event.preventDefault();
DeesContextmenu.openContextMenuWithOptions(event, [
{
name: 'New Collection',
iconName: 'lucide:folderPlus',
action: async () => {
this.selectedDatabase = dbName;
this.showCreateCollectionDialog = true;
},
},
{ divider: true },
{
name: 'Delete Database',
iconName: 'lucide:trash2',
action: async () => {
if (confirm(`Delete database "${dbName}"? This will delete all collections and documents.`)) {
const success = await apiService.dropDatabase(dbName);
if (success) {
this.databases = this.databases.filter(d => d.name !== dbName);
if (this.selectedDatabase === dbName) {
this.selectedDatabase = this.databases[0]?.name || '';
this.selectedCollection = '';
}
}
}
},
},
]);
}
render() {
return html`
<div class="app-container">
@@ -653,14 +690,9 @@ export class TsviewApp extends DeesElement {
<div
class="sidebar-item ${bucket === this.selectedBucket ? 'selected' : ''}"
@click=${() => this.selectBucket(bucket)}
@contextmenu=${(e: MouseEvent) => this.handleBucketContextMenu(e, bucket)}
>
<span class="sidebar-item-name">${bucket}</span>
<button class="delete-btn" @click=${(e: Event) => this.deleteBucket(bucket, e)} title="Delete bucket">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<polyline points="3 6 5 6 21 6"></polyline>
<path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"></path>
</svg>
</button>
</div>
`
)}
@@ -680,15 +712,6 @@ export class TsviewApp extends DeesElement {
</svg>
New Database
</button>
${this.selectedDatabase ? html`
<button class="create-btn" @click=${() => this.showCreateCollectionDialog = true}>
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<line x1="12" y1="5" x2="12" y2="19"></line>
<line x1="5" y1="12" x2="19" y2="12"></line>
</svg>
New Collection
</button>
` : ''}
<div class="sidebar-list">
${this.databases.length === 0
? html`<div class="sidebar-item" style="color: #666; cursor: default;">No databases found</div>`
@@ -715,6 +738,7 @@ export class TsviewApp extends DeesElement {
<div
class="db-group-header ${this.selectedDatabase === db.name ? 'selected' : ''}"
@click=${() => this.selectDatabase(db.name)}
@contextmenu=${(e: MouseEvent) => this.handleDatabaseContextMenu(e, db.name)}
>
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<ellipse cx="12" cy="5" rx="9" ry="3"></ellipse>
@@ -722,12 +746,6 @@ export class TsviewApp extends DeesElement {
<path d="M3 5v14c0 1.66 4 3 9 3s9-1.34 9-3V5"></path>
</svg>
<span style="flex: 1;">${db.name}</span>
<button class="delete-btn" @click=${(e: Event) => this.deleteDatabase(db.name, e)} title="Delete database">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<polyline points="3 6 5 6 21 6"></polyline>
<path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"></path>
</svg>
</button>
</div>
${this.selectedDatabase === db.name ? this.renderCollectionsList(db.name) : ''}
</div>