diff --git a/src/Controller/ConsoleApiController.php b/src/Controller/ConsoleApiController.php
index e3bdc6c..82807af 100644
--- a/src/Controller/ConsoleApiController.php
+++ b/src/Controller/ConsoleApiController.php
@@ -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') {
$data = json_decode($request->getContent(), true);
$keys = $data['keys'] ?? [];
diff --git a/templates/console/index.html.twig b/templates/console/index.html.twig
index f510f49..60727b4 100644
--- a/templates/console/index.html.twig
+++ b/templates/console/index.html.twig
@@ -529,9 +529,12 @@
+
+
+
@@ -775,6 +799,7 @@
showCreateCredentialModal: false,
showCreateBucketModal: false,
showCreatePresignedModal: false,
+ showUploadModal: false,
// Forms
newCredential: {
@@ -794,6 +819,8 @@
expires_in: 3600,
access_key: ''
},
+ uploadFile: null,
+ uploadKey: '',
init() {
this.loadStats();
@@ -991,10 +1018,10 @@
async deleteObject(bucketName, objectKey) {
if (!confirm('Are you sure you want to delete this object?')) return;
-
+
try {
- await this.apiCall(`/buckets/${bucketName}/objects/${encodeURIComponent(objectKey)}`, {
- method: 'DELETE'
+ await this.apiCall(`/buckets/${bucketName}/objects/${encodeURIComponent(objectKey)}`, {
+ method: 'DELETE'
});
this.showMessage('Object deleted successfully!');
this.viewBucket(bucketName);
@@ -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) {
// 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'}`);