diff --git a/config/routes/console_api.yaml b/config/routes/console_api.yaml index e1c04eb..01947a2 100644 --- a/config/routes/console_api.yaml +++ b/config/routes/console_api.yaml @@ -1,39 +1,92 @@ # Console API Routes -console_api_credentials: +console_api_credentials_list: path: /api/credentials - controller: App\Controller\ConsoleApiController::credentials - methods: [GET, POST] + controller: App\Controller\ConsoleApiController::listCredentials + methods: [GET] -console_api_credential_detail: +console_api_credentials_create: + path: /api/credentials + controller: App\Controller\ConsoleApiController::createCredential + methods: [POST] + +console_api_credential_get: path: /api/credentials/{id} - controller: App\Controller\ConsoleApiController::credentialDetail - methods: [GET, PUT, DELETE] + controller: App\Controller\ConsoleApiController::getCredential + methods: [GET] requirements: id: '\d+' -console_api_buckets: - path: /api/buckets - controller: App\Controller\ConsoleApiController::buckets - methods: [GET, POST] +console_api_credential_update: + path: /api/credentials/{id} + controller: App\Controller\ConsoleApiController::updateCredential + methods: [PUT] + requirements: + id: '\d+' -console_api_bucket_detail: +console_api_credential_delete: + path: /api/credentials/{id} + controller: App\Controller\ConsoleApiController::deleteCredential + methods: [DELETE] + requirements: + id: '\d+' + +console_api_buckets_list: + path: /api/buckets + controller: App\Controller\ConsoleApiController::listBuckets + methods: [GET] + +console_api_buckets_create: + path: /api/buckets + controller: App\Controller\ConsoleApiController::createBucket + methods: [POST] + +console_api_bucket_get: path: /api/buckets/{name} - controller: App\Controller\ConsoleApiController::bucketDetail - methods: [GET, DELETE] + controller: App\Controller\ConsoleApiController::getBucket + methods: [GET] requirements: name: '[a-z0-9\-\.]+' -console_api_objects: +console_api_bucket_delete: + path: /api/buckets/{name} + controller: App\Controller\ConsoleApiController::deleteBucket + methods: [DELETE] + requirements: + name: '[a-z0-9\-\.]+' + +console_api_objects_list: path: /api/buckets/{bucketName}/objects - controller: App\Controller\ConsoleApiController::objects - methods: [GET, POST, DELETE] + controller: App\Controller\ConsoleApiController::listObjects + methods: [GET] requirements: bucketName: '[a-z0-9\-\.]+' -console_api_object_detail: +console_api_create_object: + path: /api/buckets/{bucketName}/objects + controller: App\Controller\ConsoleApiController::createObject + methods: [POST] + requirements: + bucketName: '[a-z0-9\-\.]+' + +console_api_delete_objects: + path: /api/buckets/{bucketName}/objects + controller: App\Controller\ConsoleApiController::deleteObjects + methods: [DELETE] + requirements: + bucketName: '[a-z0-9\-\.]+' + +console_api_object_get: path: /api/buckets/{bucketName}/objects/{objectKey} - controller: App\Controller\ConsoleApiController::objectDetail - methods: [GET, DELETE] + controller: App\Controller\ConsoleApiController::getObject + methods: [GET] + requirements: + bucketName: '[a-z0-9\-\.]+' + objectKey: '.+' + +console_api_object_delete: + path: /api/buckets/{bucketName}/objects/{objectKey} + controller: App\Controller\ConsoleApiController::deleteObject + methods: [DELETE] requirements: bucketName: '[a-z0-9\-\.]+' objectKey: '.+' @@ -53,10 +106,15 @@ console_api_multipart_uploads: requirements: bucketName: '[a-z0-9\-\.]+' -console_api_presigned_urls: +console_api_presigned_urls_list: path: /api/presigned-urls - controller: App\Controller\ConsoleApiController::presignedUrls - methods: [GET, POST] + controller: App\Controller\ConsoleApiController::listPresignedUrls + methods: [GET] + +console_api_presigned_urls_create: + path: /api/presigned-urls + controller: App\Controller\ConsoleApiController::createPresignedUrl + methods: [POST] console_api_stats: path: /api/stats @@ -64,10 +122,15 @@ console_api_stats: methods: [GET] # Console Authentication Routes -console_login: +console_login_form: path: /console/login - controller: App\Controller\ConsoleController::login - methods: [GET, POST] + controller: App\Controller\ConsoleController::loginForm + methods: [GET] + +console_login_submit: + path: /console/login + controller: App\Controller\ConsoleController::loginSubmit + methods: [POST] console_logout: path: /console/logout diff --git a/src/Controller/ConsoleApiController.php b/src/Controller/ConsoleApiController.php index 82807af..f5736cf 100644 --- a/src/Controller/ConsoleApiController.php +++ b/src/Controller/ConsoleApiController.php @@ -28,342 +28,404 @@ class ConsoleApiController extends AbstractController } // Credentials Management - public function credentials(Request $request): JsonResponse + public function listCredentials(Request $request): JsonResponse { $resp = $this->checkAuth($request); - if (!empty($resp)) { + if ($resp !== null) { return $resp; } - if ($request->getMethod() === 'GET') { - $credentials = $this->entityManager->getRepository(S3Credential::class)->findAll(); - - return new JsonResponse([ - 'credentials' => array_map(function($cred) { - return [ - 'id' => $cred->getId(), - 'access_key' => $cred->getAccessKey(), - 'user_name' => $cred->getUserName(), - 'is_active' => $cred->isActive(), - 'created_at' => $cred->getCreatedAt()->format('Y-m-d H:i:s'), - 'bucket_count' => $cred->getBuckets()->count() - ]; - }, $credentials) - ]); - } - if ($request->getMethod() === 'POST') { - $data = json_decode($request->getContent(), true); - - $accessKey = $data['access_key'] ?? 'AKIA' . strtoupper(bin2hex(random_bytes(10))); - $secretKey = $data['secret_key'] ?? base64_encode(random_bytes(30)); - $userName = $data['user_name'] ?? null; + $credentials = $this->entityManager->getRepository(S3Credential::class)->findAll(); - $credential = $this->s3Service->createCredential($accessKey, $secretKey, $userName); - - return new JsonResponse([ - 'id' => $credential->getId(), - 'access_key' => $credential->getAccessKey(), - 'secret_key' => $credential->getSecretKey(), - 'user_name' => $credential->getUserName(), - 'is_active' => $credential->isActive(), - 'created_at' => $credential->getCreatedAt()->format('Y-m-d H:i:s') - ], 201); - } - - return new JsonResponse(['error' => 'Method not allowed'], 405); + return new JsonResponse([ + 'credentials' => array_map(function($cred) { + return [ + 'id' => $cred->getId(), + 'access_key' => $cred->getAccessKey(), + 'user_name' => $cred->getUserName(), + 'is_active' => $cred->isActive(), + 'created_at' => $cred->getCreatedAt()->format('Y-m-d H:i:s'), + 'bucket_count' => $cred->getBuckets()->count() + ]; + }, $credentials) + ]); } - public function credentialDetail(int $id, Request $request): JsonResponse + public function createCredential(Request $request): JsonResponse + { + $resp = $this->checkAuth($request); + if ($resp !== null) { + return $resp; + } + + $data = json_decode($request->getContent(), true); + + $accessKey = $data['access_key'] ?? 'AKIA' . strtoupper(bin2hex(random_bytes(10))); + $secretKey = $data['secret_key'] ?? base64_encode(random_bytes(30)); + $userName = $data['user_name'] ?? null; + + $credential = $this->s3Service->createCredential($accessKey, $secretKey, $userName); + + return new JsonResponse([ + 'id' => $credential->getId(), + 'access_key' => $credential->getAccessKey(), + 'secret_key' => $credential->getSecretKey(), + 'user_name' => $credential->getUserName(), + 'is_active' => $credential->isActive(), + 'created_at' => $credential->getCreatedAt()->format('Y-m-d H:i:s') + ], 201); + } + + public function getCredential(int $id, Request $request): JsonResponse { $authResp = $this->checkAuth($request); if ($authResp !== null) { return $authResp; } $credential = $this->entityManager->getRepository(S3Credential::class)->find($id); - + if (!$credential) { return new JsonResponse(['error' => 'Credential not found'], 404); } - if ($request->getMethod() === 'GET') { - return new JsonResponse([ - 'id' => $credential->getId(), - 'access_key' => $credential->getAccessKey(), - 'secret_key' => $credential->getSecretKey(), - 'user_name' => $credential->getUserName(), - 'is_active' => $credential->isActive(), - 'created_at' => $credential->getCreatedAt()->format('Y-m-d H:i:s'), - 'buckets' => array_map(function($bucket) { - return [ - 'name' => $bucket->getName(), - 'region' => $bucket->getRegion(), - 'created_at' => $bucket->getCreatedAt()->format('Y-m-d H:i:s') - ]; - }, $credential->getBuckets()->toArray()) - ]); - } - - if ($request->getMethod() === 'PUT') { - $data = json_decode($request->getContent(), true); - - if (isset($data['user_name'])) { - $credential->setUserName($data['user_name']); - } - if (isset($data['is_active'])) { - $credential->setIsActive($data['is_active']); - } - - $this->entityManager->flush(); - - return new JsonResponse(['message' => 'Credential updated']); - } - - if ($request->getMethod() === 'DELETE') { - $this->entityManager->remove($credential); - $this->entityManager->flush(); - - return new JsonResponse(['message' => 'Credential deleted']); - } - - return new JsonResponse(['error' => 'Method not allowed'], 405); + return new JsonResponse([ + 'id' => $credential->getId(), + 'access_key' => $credential->getAccessKey(), + 'secret_key' => $credential->getSecretKey(), + 'user_name' => $credential->getUserName(), + 'is_active' => $credential->isActive(), + 'created_at' => $credential->getCreatedAt()->format('Y-m-d H:i:s'), + 'buckets' => array_map(function($bucket) { + return [ + 'name' => $bucket->getName(), + 'region' => $bucket->getRegion(), + 'created_at' => $bucket->getCreatedAt()->format('Y-m-d H:i:s') + ]; + }, $credential->getBuckets()->toArray()) + ]); } - // Buckets Management - public function buckets(Request $request): JsonResponse + public function updateCredential(int $id, Request $request): JsonResponse { $authResp = $this->checkAuth($request); if ($authResp !== null) { return $authResp; } - if ($request->getMethod() === 'GET') { - $buckets = $this->entityManager->getRepository(S3Bucket::class)->findAll(); - - return new JsonResponse([ - 'buckets' => array_map(function($bucket) { - $objectCount = $this->entityManager->getRepository(S3Object::class)->count(['bucket' => $bucket]); - $totalSize = $this->entityManager->createQueryBuilder() - ->select('SUM(o.size)') - ->from(S3Object::class, 'o') - ->where('o.bucket = :bucket') - ->setParameter('bucket', $bucket) - ->getQuery() - ->getSingleScalarResult() ?: 0; + $credential = $this->entityManager->getRepository(S3Credential::class)->find($id); - return [ - 'name' => $bucket->getName(), - 'region' => $bucket->getRegion(), - 'owner' => $bucket->getOwner()->getUserName() ?: $bucket->getOwner()->getAccessKey(), - 'created_at' => $bucket->getCreatedAt()->format('Y-m-d H:i:s'), - 'object_count' => $objectCount, - 'total_size' => $totalSize, - 'total_size_human' => $this->formatBytes($totalSize) - ]; - }, $buckets) - ]); + if (!$credential) { + return new JsonResponse(['error' => 'Credential not found'], 404); } - if ($request->getMethod() === 'POST') { - $data = json_decode($request->getContent(), true); - - $bucketName = $data['name'] ?? null; - $ownerId = $data['owner_id'] ?? null; - $region = $data['region'] ?? ($_ENV['APP_REGION'] ?? 'us-east-1'); + $data = json_decode($request->getContent(), true); - if (!$bucketName || !$ownerId) { - return new JsonResponse(['error' => 'Missing bucket name or owner'], 400); - } + if (isset($data['user_name'])) { + $credential->setUserName($data['user_name']); + } + if (isset($data['is_active'])) { + $credential->setIsActive($data['is_active']); + } - $owner = $this->entityManager->getRepository(S3Credential::class)->find($ownerId); - if (!$owner) { - return new JsonResponse(['error' => 'Owner not found'], 404); - } + $this->entityManager->flush(); - try { - $bucket = $this->s3Service->createBucket($bucketName, $owner, $region); - - return new JsonResponse([ + return new JsonResponse(['message' => 'Credential updated']); + } + + public function deleteCredential(int $id, Request $request): JsonResponse + { + $authResp = $this->checkAuth($request); + if ($authResp !== null) { + return $authResp; + } + $credential = $this->entityManager->getRepository(S3Credential::class)->find($id); + + if (!$credential) { + return new JsonResponse(['error' => 'Credential not found'], 404); + } + + $this->entityManager->remove($credential); + $this->entityManager->flush(); + + return new JsonResponse(['message' => 'Credential deleted']); + } + + // Buckets Management + public function listBuckets(Request $request): JsonResponse + { + $authResp = $this->checkAuth($request); + if ($authResp !== null) { + return $authResp; + } + + $buckets = $this->entityManager->getRepository(S3Bucket::class)->findAll(); + + return new JsonResponse([ + 'buckets' => array_map(function($bucket) { + $objectCount = $this->entityManager->getRepository(S3Object::class)->count(['bucket' => $bucket]); + $totalSize = $this->entityManager->createQueryBuilder() + ->select('SUM(o.size)') + ->from(S3Object::class, 'o') + ->where('o.bucket = :bucket') + ->setParameter('bucket', $bucket) + ->getQuery() + ->getSingleScalarResult() ?: 0; + + return [ 'name' => $bucket->getName(), 'region' => $bucket->getRegion(), 'owner' => $bucket->getOwner()->getUserName() ?: $bucket->getOwner()->getAccessKey(), - 'created_at' => $bucket->getCreatedAt()->format('Y-m-d H:i:s') - ], 201); - } catch (\Exception $e) { - return new JsonResponse(['error' => $e->getMessage()], 400); - } - } - - return new JsonResponse(['error' => 'Method not allowed'], 405); + 'created_at' => $bucket->getCreatedAt()->format('Y-m-d H:i:s'), + 'object_count' => $objectCount, + 'total_size' => $totalSize, + 'total_size_human' => $this->formatBytes($totalSize) + ]; + }, $buckets) + ]); } - public function bucketDetail(string $name, Request $request): JsonResponse + public function createBucket(Request $request): JsonResponse + { + $authResp = $this->checkAuth($request); + if ($authResp !== null) { + return $authResp; + } + + $data = json_decode($request->getContent(), true); + + $bucketName = $data['name'] ?? null; + $ownerId = $data['owner_id'] ?? null; + $region = $data['region'] ?? ($_ENV['APP_REGION'] ?? 'us-east-1'); + + if (!$bucketName || !$ownerId) { + return new JsonResponse(['error' => 'Missing bucket name or owner'], 400); + } + + $owner = $this->entityManager->getRepository(S3Credential::class)->find($ownerId); + if (!$owner) { + return new JsonResponse(['error' => 'Owner not found'], 404); + } + + try { + $bucket = $this->s3Service->createBucket($bucketName, $owner, $region); + + return new JsonResponse([ + 'name' => $bucket->getName(), + 'region' => $bucket->getRegion(), + 'owner' => $bucket->getOwner()->getUserName() ?: $bucket->getOwner()->getAccessKey(), + 'created_at' => $bucket->getCreatedAt()->format('Y-m-d H:i:s') + ], 201); + } catch (\Exception $e) { + return new JsonResponse(['error' => $e->getMessage()], 400); + } + } + + public function getBucket(string $name, Request $request): JsonResponse { $authResp = $this->checkAuth($request); if ($authResp !== null) { return $authResp; } $bucket = $this->s3Service->findBucketByName($name); - + if (!$bucket) { return new JsonResponse(['error' => 'Bucket not found'], 404); } - if ($request->getMethod() === 'GET') { - $objects = $this->s3Service->listObjects($bucket); - $totalSize = array_sum(array_map(fn($obj) => $obj->getSize(), $objects)); + $objects = $this->s3Service->listObjects($bucket); + $totalSize = array_sum(array_map(fn($obj) => $obj->getSize(), $objects)); - return new JsonResponse([ - 'name' => $bucket->getName(), - 'region' => $bucket->getRegion(), - 'owner' => $bucket->getOwner()->getUserName() ?: $bucket->getOwner()->getAccessKey(), - 'created_at' => $bucket->getCreatedAt()->format('Y-m-d H:i:s'), - 'object_count' => count($objects), - 'total_size' => $totalSize, - 'total_size_human' => $this->formatBytes($totalSize), - 'objects' => array_map(function($obj) { - return [ - 'key' => $obj->getObjectKey(), - 'size' => $obj->getSize(), - 'size_human' => $this->formatBytes($obj->getSize()), - 'content_type' => $obj->getContentType(), - 'etag' => $obj->getEtag(), - 'is_multipart' => $obj->isMultipart(), - 'created_at' => $obj->getCreatedAt()->format('Y-m-d H:i:s') - ]; - }, $objects) - ]); + return new JsonResponse([ + 'name' => $bucket->getName(), + 'region' => $bucket->getRegion(), + 'owner' => $bucket->getOwner()->getUserName() ?: $bucket->getOwner()->getAccessKey(), + 'created_at' => $bucket->getCreatedAt()->format('Y-m-d H:i:s'), + 'object_count' => count($objects), + 'total_size' => $totalSize, + 'total_size_human' => $this->formatBytes($totalSize), + 'objects' => array_map(function($obj) { + return [ + 'key' => $obj->getObjectKey(), + 'size' => $obj->getSize(), + 'size_human' => $this->formatBytes($obj->getSize()), + 'content_type' => $obj->getContentType(), + 'etag' => $obj->getEtag(), + 'is_multipart' => $obj->isMultipart(), + 'created_at' => $obj->getCreatedAt()->format('Y-m-d H:i:s') + ]; + }, $objects) + ]); + } + + public function deleteBucket(string $name, Request $request): JsonResponse + { + $authResp = $this->checkAuth($request); + if ($authResp !== null) { + return $authResp; + } + $bucket = $this->s3Service->findBucketByName($name); + + if (!$bucket) { + return new JsonResponse(['error' => 'Bucket not found'], 404); } - if ($request->getMethod() === 'DELETE') { - try { - $this->s3Service->deleteBucket($bucket); - return new JsonResponse(['message' => 'Bucket deleted']); - } catch (\Exception $e) { - return new JsonResponse(['error' => $e->getMessage()], 400); - } + try { + $this->s3Service->deleteBucket($bucket); + return new JsonResponse(['message' => 'Bucket deleted']); + } catch (\Exception $e) { + return new JsonResponse(['error' => $e->getMessage()], 400); } - - return new JsonResponse(['error' => 'Method not allowed'], 405); } // Objects Management - public function objects(string $bucketName, Request $request): JsonResponse + public function listObjects(string $bucketName, Request $request): JsonResponse { $authResp = $this->checkAuth($request); if ($authResp !== null) { return $authResp; } $bucket = $this->s3Service->findBucketByName($bucketName); - + if (!$bucket) { return new JsonResponse(['error' => 'Bucket not found'], 404); } - if ($request->getMethod() === 'GET') { - $prefix = $request->query->get('prefix', ''); - $objects = $this->s3Service->listObjects($bucket, $prefix); - - return new JsonResponse([ - 'objects' => array_map(function($obj) { - return [ - 'key' => $obj->getObjectKey(), - 'size' => $obj->getSize(), - 'size_human' => $this->formatBytes($obj->getSize()), - 'content_type' => $obj->getContentType(), - 'etag' => $obj->getEtag(), - 'is_multipart' => $obj->isMultipart(), - 'created_at' => $obj->getCreatedAt()->format('Y-m-d H:i:s') - ]; - }, $objects) - ]); - } + $prefix = $request->query->get('prefix', ''); + $objects = $this->s3Service->listObjects($bucket, $prefix); - 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'] ?? []; - - $deleted = []; - foreach ($keys as $key) { - $object = $this->s3Service->findObject($bucket, $key); - if ($object) { - $this->s3Service->deleteObject($object); - $deleted[] = $key; - } - } - - return new JsonResponse(['deleted' => $deleted]); - } - - return new JsonResponse(['error' => 'Method not allowed'], 405); + return new JsonResponse([ + 'objects' => array_map(function($obj) { + return [ + 'key' => $obj->getObjectKey(), + 'size' => $obj->getSize(), + 'size_human' => $this->formatBytes($obj->getSize()), + 'content_type' => $obj->getContentType(), + 'etag' => $obj->getEtag(), + 'is_multipart' => $obj->isMultipart(), + 'created_at' => $obj->getCreatedAt()->format('Y-m-d H:i:s') + ]; + }, $objects) + ]); } - public function objectDetail(string $bucketName, string $objectKey, Request $request): JsonResponse + public function createObject(string $bucketName, Request $request): JsonResponse { $authResp = $this->checkAuth($request); if ($authResp !== null) { return $authResp; } $bucket = $this->s3Service->findBucketByName($bucketName); - + + if (!$bucket) { + return new JsonResponse(['error' => 'Bucket not found'], 404); + } + + $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); + } + + public function deleteObjects(string $bucketName, Request $request): JsonResponse + { + $authResp = $this->checkAuth($request); + if ($authResp !== null) { + return $authResp; + } + $bucket = $this->s3Service->findBucketByName($bucketName); + + if (!$bucket) { + return new JsonResponse(['error' => 'Bucket not found'], 404); + } + + $data = json_decode($request->getContent(), true); + $keys = $data['keys'] ?? []; + + $deleted = []; + foreach ($keys as $key) { + $object = $this->s3Service->findObject($bucket, $key); + if ($object) { + $this->s3Service->deleteObject($object); + $deleted[] = $key; + } + } + + return new JsonResponse(['deleted' => $deleted]); + } + + public function getObject(string $bucketName, string $objectKey, Request $request): JsonResponse + { + $authResp = $this->checkAuth($request); + if ($authResp !== null) { + return $authResp; + } + $bucket = $this->s3Service->findBucketByName($bucketName); + if (!$bucket) { return new JsonResponse(['error' => 'Bucket not found'], 404); } $object = $this->s3Service->findObject($bucket, $objectKey); - + if (!$object) { return new JsonResponse(['error' => 'Object not found'], 404); } - if ($request->getMethod() === 'GET') { - return new JsonResponse([ - 'key' => $object->getObjectKey(), - 'size' => $object->getSize(), - 'size_human' => $this->formatBytes($object->getSize()), - 'content_type' => $object->getContentType(), - 'etag' => $object->getEtag(), - 'is_multipart' => $object->isMultipart(), - 'part_count' => $object->getPartCount(), - 'metadata' => $object->getMetadata(), - 'storage_path' => $object->getStoragePath(), - 'created_at' => $object->getCreatedAt()->format('Y-m-d H:i:s'), - 'updated_at' => $object->getUpdatedAt()->format('Y-m-d H:i:s') - ]); + return new JsonResponse([ + 'key' => $object->getObjectKey(), + 'size' => $object->getSize(), + 'size_human' => $this->formatBytes($object->getSize()), + 'content_type' => $object->getContentType(), + 'etag' => $object->getEtag(), + 'is_multipart' => $object->isMultipart(), + 'part_count' => $object->getPartCount(), + 'metadata' => $object->getMetadata(), + 'storage_path' => $object->getStoragePath(), + 'created_at' => $object->getCreatedAt()->format('Y-m-d H:i:s'), + 'updated_at' => $object->getUpdatedAt()->format('Y-m-d H:i:s') + ]); + } + + public function deleteObject(string $bucketName, string $objectKey, Request $request): JsonResponse + { + $authResp = $this->checkAuth($request); + if ($authResp !== null) { + return $authResp; + } + $bucket = $this->s3Service->findBucketByName($bucketName); + + if (!$bucket) { + return new JsonResponse(['error' => 'Bucket not found'], 404); } - if ($request->getMethod() === 'DELETE') { - $this->s3Service->deleteObject($object); - return new JsonResponse(['message' => 'Object deleted']); + $object = $this->s3Service->findObject($bucket, $objectKey); + + if (!$object) { + return new JsonResponse(['error' => 'Object not found'], 404); } - return new JsonResponse(['error' => 'Method not allowed'], 405); + $this->s3Service->deleteObject($object); + return new JsonResponse(['message' => 'Object deleted']); } public function createPresignedUpload(string $bucketName, string $objectKey, Request $request): JsonResponse @@ -435,61 +497,64 @@ class ConsoleApiController extends AbstractController } // Presigned URLs - public function presignedUrls(Request $request): JsonResponse + public function listPresignedUrls(Request $request): JsonResponse { $authResp = $this->checkAuth($request); if ($authResp !== null) { return $authResp; } - if ($request->getMethod() === 'GET') { - $urls = $this->entityManager->getRepository(\App\Entity\S3PresignedUrl::class) - ->createQueryBuilder('p') - ->where('p.expiresAt > :now') - ->setParameter('now', new \DateTime()) - ->orderBy('p.createdAt', 'DESC') - ->setMaxResults(100) - ->getQuery() - ->getResult(); - return new JsonResponse([ - 'urls' => array_map(function($url) { - return [ - 'bucket_name' => $url->getBucketName(), - 'object_key' => $url->getObjectKey(), - 'method' => $url->getMethod(), - 'access_key' => $url->getAccessKey(), - 'expires_at' => $url->getExpiresAt()->format('Y-m-d H:i:s'), - 'created_at' => $url->getCreatedAt()->format('Y-m-d H:i:s'), - 'url' => $url->getUrl() - ]; - }, $urls) - ]); + $urls = $this->entityManager->getRepository(\App\Entity\S3PresignedUrl::class) + ->createQueryBuilder('p') + ->where('p.expiresAt > :now') + ->setParameter('now', new \DateTime()) + ->orderBy('p.createdAt', 'DESC') + ->setMaxResults(100) + ->getQuery() + ->getResult(); + + return new JsonResponse([ + 'urls' => array_map(function($url) { + return [ + 'bucket_name' => $url->getBucketName(), + 'object_key' => $url->getObjectKey(), + 'method' => $url->getMethod(), + 'access_key' => $url->getAccessKey(), + 'expires_at' => $url->getExpiresAt()->format('Y-m-d H:i:s'), + 'created_at' => $url->getCreatedAt()->format('Y-m-d H:i:s'), + 'url' => $url->getUrl() + ]; + }, $urls) + ]); + } + + public function createPresignedUrl(Request $request): JsonResponse + { + $authResp = $this->checkAuth($request); + if ($authResp !== null) { + return $authResp; } - if ($request->getMethod() === 'POST') { - $data = json_decode($request->getContent(), true); - - $bucketName = $data['bucket_name'] ?? null; - $objectKey = $data['object_key'] ?? null; - $method = $data['method'] ?? 'GET'; - $expiresIn = $data['expires_in'] ?? 3600; - $accessKey = $data['access_key'] ?? null; + $data = json_decode($request->getContent(), true); - if (!$bucketName || !$objectKey || !$accessKey) { - return new JsonResponse(['error' => 'Missing required fields'], 400); - } + $bucketName = $data['bucket_name'] ?? null; + $objectKey = $data['object_key'] ?? null; + $method = $data['method'] ?? 'GET'; + $expiresIn = $data['expires_in'] ?? 3600; + $accessKey = $data['access_key'] ?? null; - $credential = $this->s3Service->findCredentialByAccessKey($accessKey); - if (!$credential) { - return new JsonResponse(['error' => 'Invalid access key'], 404); - } - - $url = $this->s3Service->generatePresignedUrl($bucketName, $objectKey, $credential, $method, $expiresIn); - - return new JsonResponse(['url' => $url], 201); + if (!$bucketName || !$objectKey || !$accessKey) { + return new JsonResponse(['error' => 'Missing required fields'], 400); } - return new JsonResponse(['error' => 'Method not allowed'], 405); + $credential = $this->s3Service->findCredentialByAccessKey($accessKey); + if (!$credential) { + return new JsonResponse(['error' => 'Invalid access key'], 404); + } + + $url = $this->s3Service->generatePresignedUrl($bucketName, $objectKey, $credential, $method, $expiresIn); + + return new JsonResponse(['url' => $url], 201); } // Statistics diff --git a/src/Controller/ConsoleController.php b/src/Controller/ConsoleController.php index 585b158..d46b52d 100644 --- a/src/Controller/ConsoleController.php +++ b/src/Controller/ConsoleController.php @@ -18,28 +18,32 @@ class ConsoleController extends AbstractController return $this->render('console/index.html.twig'); } - public function login(Request $request): Response + public function loginForm(Request $request): Response { if ($request->getSession()->get('console_logged_in')) { return new RedirectResponse('/console'); } - $error = null; - if ($request->isMethod('POST')) { - $user = $request->request->get('username'); - $pass = $request->request->get('password'); - $envUser = $_ENV['CONSOLE_USER'] ?? 'admin'; - $envPass = $_ENV['CONSOLE_PASS'] ?? 'password'; + return $this->render('console/login.html.twig', ['error' => null]); + } - if ($user === $envUser && $pass === $envPass) { - $request->getSession()->set('console_logged_in', true); - return new RedirectResponse('/console'); - } - - $error = 'Invalid credentials'; + public function loginSubmit(Request $request): Response + { + if ($request->getSession()->get('console_logged_in')) { + return new RedirectResponse('/console'); } - return $this->render('console/login.html.twig', ['error' => $error]); + $user = $request->request->get('username'); + $pass = $request->request->get('password'); + $envUser = $_ENV['CONSOLE_USER'] ?? 'admin'; + $envPass = $_ENV['CONSOLE_PASS'] ?? 'password'; + + if ($user === $envUser && $pass === $envPass) { + $request->getSession()->set('console_logged_in', true); + return new RedirectResponse('/console'); + } + + return $this->render('console/login.html.twig', ['error' => 'Invalid credentials']); } public function logout(Request $request): Response