Add presigned URL management

This commit is contained in:
biondizzle
2025-06-05 10:07:14 -04:00
parent 994b08d44c
commit c5677539e4
7 changed files with 228 additions and 17 deletions

View File

@@ -577,6 +577,59 @@
</div>
</div>
<!-- Presigned URLs Section -->
<div x-show="activeSection === 'presigned'" x-cloak class="fade-in">
<div class="header">
<h2><i class="fas fa-link"></i> Presigned URLs</h2>
<button @click="showCreatePresignedModal = true; loadBuckets(); loadCredentials()" class="btn btn-primary">
<i class="fas fa-plus"></i> Create URL
</button>
</div>
<div x-show="message" x-text="message" :class="messageType === 'error' ? 'error' : 'success'"></div>
<div class="card">
<div class="card-body">
<div x-show="loading" class="loading">Loading URLs...</div>
<template x-if="!loading && presignedUrls.length === 0">
<div class="empty-state">
<i class="fas fa-link"></i>
<h3>No presigned URLs found</h3>
<p>Create a presigned URL to share temporary access.</p>
</div>
</template>
<template x-if="!loading && presignedUrls.length > 0">
<table class="table">
<thead>
<tr>
<th>Bucket</th>
<th>Object</th>
<th>Method</th>
<th>Expires</th>
<th>URL</th>
</tr>
</thead>
<tbody>
<template x-for="p in presignedUrls" :key="p.url">
<tr>
<td x-text="p.bucket_name"></td>
<td x-text="p.object_key"></td>
<td x-text="p.method"></td>
<td x-text="p.expires_at"></td>
<td>
<input class="form-input" :value="p.url" readonly style="font-size: 0.8rem;">
</td>
</tr>
</template>
</tbody>
</table>
</template>
</div>
</div>
</div>
<!-- Create Credential Modal -->
<div x-show="showCreateCredentialModal" x-cloak class="modal" @click.self="showCreateCredentialModal = false">
<div class="modal-content">
@@ -646,6 +699,58 @@
</form>
</div>
</div>
<!-- Create Presigned URL Modal -->
<div x-show="showCreatePresignedModal" x-cloak class="modal" @click.self="showCreatePresignedModal = false">
<div class="modal-content">
<h3 style="margin-bottom: 1rem;">Create Presigned URL</h3>
<form @submit.prevent="createPresigned()">
<div class="form-group">
<label class="form-label">Bucket Name</label>
<select x-model="newPresigned.bucket_name" class="form-select" required>
<option value="">Select bucket...</option>
<template x-for="bucket in buckets" :key="bucket.name">
<option :value="bucket.name" x-text="bucket.name"></option>
</template>
</select>
</div>
<div class="form-group">
<label class="form-label">Object Key</label>
<input x-model="newPresigned.object_key" type="text" class="form-input" placeholder="path/to/file.txt" required>
</div>
<div class="form-group">
<label class="form-label">Method</label>
<select x-model="newPresigned.method" class="form-select">
<option value="GET">GET</option>
<option value="PUT">PUT</option>
<option value="POST">POST</option>
<option value="DELETE">DELETE</option>
</select>
</div>
<div class="form-group">
<label class="form-label">Expires In (seconds)</label>
<input x-model="newPresigned.expires_in" type="number" class="form-input" min="1" max="604800" required>
</div>
<div class="form-group">
<label class="form-label">Access Key</label>
<select x-model="newPresigned.access_key" class="form-select" required>
<option value="">Select credential...</option>
<template x-for="credential in credentials" :key="credential.id">
<option :value="credential.access_key" x-text="credential.user_name || credential.access_key"></option>
</template>
</select>
</div>
<div style="display: flex; gap: 1rem; justify-content: flex-end;">
<button type="button" @click="showCreatePresignedModal = false" class="btn btn-secondary">
Cancel
</button>
<button type="submit" class="btn btn-primary">
Create
</button>
</div>
</form>
</div>
</div>
</main>
</div>
@@ -664,10 +769,12 @@
selectedBucket: null,
filteredObjects: [],
objectSearch: '',
presignedUrls: [],
// Modals
showCreateCredentialModal: false,
showCreateBucketModal: false,
showCreatePresignedModal: false,
// Forms
newCredential: {
@@ -680,6 +787,13 @@
owner_id: '',
region: 'us-east-1'
},
newPresigned: {
bucket_name: '',
object_key: '',
method: 'GET',
expires_in: 3600,
access_key: ''
},
init() {
this.loadStats();
@@ -699,6 +813,9 @@
case 'buckets':
this.loadBuckets();
break;
case 'presigned':
this.loadPresignedUrls();
break;
}
},
@@ -795,6 +912,39 @@
}
},
async loadPresignedUrls() {
this.loading = true;
try {
const response = await this.apiCall('/presigned-urls');
this.presignedUrls = response.urls;
} catch (error) {
console.error('Failed to load presigned URLs:', error);
}
this.loading = false;
},
async createPresigned() {
try {
const payload = { ...this.newPresigned };
const urlData = await this.apiCall('/presigned-urls', {
method: 'POST',
body: JSON.stringify(payload)
});
this.showMessage('Presigned URL created');
this.showCreatePresignedModal = false;
this.newPresigned = { bucket_name: '', object_key: '', method: 'GET', expires_in: 3600, access_key: '' };
this.presignedUrls.unshift({
bucket_name: payload.bucket_name,
object_key: payload.object_key,
method: payload.method,
expires_at: new Date(Date.now() + payload.expires_in * 1000).toISOString().slice(0,19).replace('T',' '),
url: urlData.url
});
} catch (error) {
console.error('Failed to create presigned URL:', error);
}
},
async deleteCredential(id) {
if (!confirm('Are you sure you want to delete this credential?')) return;