Add object upload feature to console
This commit is contained in:
@@ -277,6 +277,34 @@ class ConsoleApiController extends AbstractController
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($request->getMethod() === 'POST') {
|
||||||
|
$objectKey = $request->headers->get('X-Object-Key')
|
||||||
|
?? $request->query->get('key')
|
||||||
|
?? $request->request->get('object_key');
|
||||||
|
|
||||||
|
if (empty($objectKey)) {
|
||||||
|
return new JsonResponse(['error' => 'Missing object key'], 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
$contentType = $request->headers->get('Content-Type', 'application/octet-stream');
|
||||||
|
$file = $request->files->get('file');
|
||||||
|
if ($file instanceof \Symfony\Component\HttpFoundation\File\UploadedFile) {
|
||||||
|
$contentType = $file->getMimeType() ?: $contentType;
|
||||||
|
$content = file_get_contents($file->getPathname());
|
||||||
|
} else {
|
||||||
|
$content = $request->getContent();
|
||||||
|
}
|
||||||
|
|
||||||
|
$object = $this->s3Service->putObject($bucket, $objectKey, $content, $contentType);
|
||||||
|
|
||||||
|
return new JsonResponse([
|
||||||
|
'key' => $object->getObjectKey(),
|
||||||
|
'etag' => $object->getEtag(),
|
||||||
|
'size' => $object->getSize(),
|
||||||
|
'content_type' => $object->getContentType()
|
||||||
|
], 201);
|
||||||
|
}
|
||||||
|
|
||||||
if ($request->getMethod() === 'DELETE') {
|
if ($request->getMethod() === 'DELETE') {
|
||||||
$data = json_decode($request->getContent(), true);
|
$data = json_decode($request->getContent(), true);
|
||||||
$keys = $data['keys'] ?? [];
|
$keys = $data['keys'] ?? [];
|
||||||
|
|||||||
@@ -529,9 +529,12 @@
|
|||||||
<div x-show="selectedBucket" class="card">
|
<div x-show="selectedBucket" class="card">
|
||||||
<div class="card-header">
|
<div class="card-header">
|
||||||
Objects
|
Objects
|
||||||
<div>
|
<div style="display: flex; gap: 0.5rem;">
|
||||||
<input x-model="objectSearch" @input="filterObjects()"
|
<input x-model="objectSearch" @input="filterObjects()"
|
||||||
placeholder="Search objects..." class="search-input">
|
placeholder="Search objects..." class="search-input">
|
||||||
|
<button @click="showUploadModal = true" class="btn btn-primary btn-sm">
|
||||||
|
<i class="fas fa-upload"></i> Upload
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
@@ -630,6 +633,27 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Upload Object Modal -->
|
||||||
|
<div x-show="showUploadModal" x-cloak class="modal" @click.self="showUploadModal = false">
|
||||||
|
<div class="modal-content">
|
||||||
|
<h3 style="margin-bottom: 1rem;">Upload Object</h3>
|
||||||
|
<form @submit.prevent="uploadObject()">
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label">Object Key</label>
|
||||||
|
<input x-model="uploadKey" type="text" class="form-input" placeholder="path/to/file.txt" required>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label">Select File</label>
|
||||||
|
<input type="file" @change="uploadFile = $event.target.files[0]" class="form-input" required>
|
||||||
|
</div>
|
||||||
|
<div style="display: flex; gap: 1rem; justify-content: flex-end;">
|
||||||
|
<button type="button" @click="showUploadModal = false" class="btn btn-secondary">Cancel</button>
|
||||||
|
<button type="submit" class="btn btn-primary">Upload</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Create Credential Modal -->
|
<!-- Create Credential Modal -->
|
||||||
<div x-show="showCreateCredentialModal" x-cloak class="modal" @click.self="showCreateCredentialModal = false">
|
<div x-show="showCreateCredentialModal" x-cloak class="modal" @click.self="showCreateCredentialModal = false">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
@@ -775,6 +799,7 @@
|
|||||||
showCreateCredentialModal: false,
|
showCreateCredentialModal: false,
|
||||||
showCreateBucketModal: false,
|
showCreateBucketModal: false,
|
||||||
showCreatePresignedModal: false,
|
showCreatePresignedModal: false,
|
||||||
|
showUploadModal: false,
|
||||||
|
|
||||||
// Forms
|
// Forms
|
||||||
newCredential: {
|
newCredential: {
|
||||||
@@ -794,6 +819,8 @@
|
|||||||
expires_in: 3600,
|
expires_in: 3600,
|
||||||
access_key: ''
|
access_key: ''
|
||||||
},
|
},
|
||||||
|
uploadFile: null,
|
||||||
|
uploadKey: '',
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
this.loadStats();
|
this.loadStats();
|
||||||
@@ -1003,6 +1030,29 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
async uploadObject() {
|
||||||
|
if (!this.uploadFile || !this.uploadKey) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
await fetch(`/api/buckets/${this.selectedBucket.name}/objects?key=${encodeURIComponent(this.uploadKey)}`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': this.uploadFile.type || 'application/octet-stream',
|
||||||
|
'X-Object-Key': this.uploadKey
|
||||||
|
},
|
||||||
|
body: this.uploadFile
|
||||||
|
});
|
||||||
|
this.showMessage('Object uploaded successfully!');
|
||||||
|
this.showUploadModal = false;
|
||||||
|
this.uploadFile = null;
|
||||||
|
this.uploadKey = '';
|
||||||
|
this.viewBucket(this.selectedBucket.name);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to upload object:', error);
|
||||||
|
this.showMessage('Upload failed', 'error');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
async viewCredential(credential) {
|
async viewCredential(credential) {
|
||||||
// Could open a modal with full credential details
|
// Could open a modal with full credential details
|
||||||
alert(`Access Key: ${credential.access_key}\nUser: ${credential.user_name || 'N/A'}\nStatus: ${credential.is_active ? 'Active' : 'Inactive'}`);
|
alert(`Access Key: ${credential.access_key}\nUser: ${credential.user_name || 'N/A'}\nStatus: ${credential.is_active ? 'Active' : 'Inactive'}`);
|
||||||
|
|||||||
Reference in New Issue
Block a user