Merge pull request #9 from biondizzle/codex/refactor-route-handling-to-use-config/routes

Refactor API controllers to use explicit routes
This commit is contained in:
biondizzle
2025-06-05 21:07:12 -04:00
committed by GitHub
3 changed files with 465 additions and 333 deletions

View File

@@ -1,39 +1,92 @@
# Console API Routes # Console API Routes
console_api_credentials: console_api_credentials_list:
path: /api/credentials path: /api/credentials
controller: App\Controller\ConsoleApiController::credentials controller: App\Controller\ConsoleApiController::listCredentials
methods: [GET, POST] 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} path: /api/credentials/{id}
controller: App\Controller\ConsoleApiController::credentialDetail controller: App\Controller\ConsoleApiController::getCredential
methods: [GET, PUT, DELETE] methods: [GET]
requirements: requirements:
id: '\d+' id: '\d+'
console_api_buckets: console_api_credential_update:
path: /api/buckets path: /api/credentials/{id}
controller: App\Controller\ConsoleApiController::buckets controller: App\Controller\ConsoleApiController::updateCredential
methods: [GET, POST] 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} path: /api/buckets/{name}
controller: App\Controller\ConsoleApiController::bucketDetail controller: App\Controller\ConsoleApiController::getBucket
methods: [GET, DELETE] methods: [GET]
requirements: requirements:
name: '[a-z0-9\-\.]+' 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 path: /api/buckets/{bucketName}/objects
controller: App\Controller\ConsoleApiController::objects controller: App\Controller\ConsoleApiController::listObjects
methods: [GET, POST, DELETE] methods: [GET]
requirements: requirements:
bucketName: '[a-z0-9\-\.]+' 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} path: /api/buckets/{bucketName}/objects/{objectKey}
controller: App\Controller\ConsoleApiController::objectDetail controller: App\Controller\ConsoleApiController::getObject
methods: [GET, DELETE] 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: requirements:
bucketName: '[a-z0-9\-\.]+' bucketName: '[a-z0-9\-\.]+'
objectKey: '.+' objectKey: '.+'
@@ -53,10 +106,15 @@ console_api_multipart_uploads:
requirements: requirements:
bucketName: '[a-z0-9\-\.]+' bucketName: '[a-z0-9\-\.]+'
console_api_presigned_urls: console_api_presigned_urls_list:
path: /api/presigned-urls path: /api/presigned-urls
controller: App\Controller\ConsoleApiController::presignedUrls controller: App\Controller\ConsoleApiController::listPresignedUrls
methods: [GET, POST] methods: [GET]
console_api_presigned_urls_create:
path: /api/presigned-urls
controller: App\Controller\ConsoleApiController::createPresignedUrl
methods: [POST]
console_api_stats: console_api_stats:
path: /api/stats path: /api/stats
@@ -64,10 +122,15 @@ console_api_stats:
methods: [GET] methods: [GET]
# Console Authentication Routes # Console Authentication Routes
console_login: console_login_form:
path: /console/login path: /console/login
controller: App\Controller\ConsoleController::login controller: App\Controller\ConsoleController::loginForm
methods: [GET, POST] methods: [GET]
console_login_submit:
path: /console/login
controller: App\Controller\ConsoleController::loginSubmit
methods: [POST]
console_logout: console_logout:
path: /console/logout path: /console/logout

View File

@@ -28,342 +28,404 @@ class ConsoleApiController extends AbstractController
} }
// Credentials Management // Credentials Management
public function credentials(Request $request): JsonResponse public function listCredentials(Request $request): JsonResponse
{ {
$resp = $this->checkAuth($request); $resp = $this->checkAuth($request);
if (!empty($resp)) { if ($resp !== null) {
return $resp; 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') { $credentials = $this->entityManager->getRepository(S3Credential::class)->findAll();
$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([
'credentials' => array_map(function($cred) {
return new JsonResponse([ return [
'id' => $credential->getId(), 'id' => $cred->getId(),
'access_key' => $credential->getAccessKey(), 'access_key' => $cred->getAccessKey(),
'secret_key' => $credential->getSecretKey(), 'user_name' => $cred->getUserName(),
'user_name' => $credential->getUserName(), 'is_active' => $cred->isActive(),
'is_active' => $credential->isActive(), 'created_at' => $cred->getCreatedAt()->format('Y-m-d H:i:s'),
'created_at' => $credential->getCreatedAt()->format('Y-m-d H:i:s') 'bucket_count' => $cred->getBuckets()->count()
], 201); ];
} }, $credentials)
]);
return new JsonResponse(['error' => 'Method not allowed'], 405);
} }
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); $authResp = $this->checkAuth($request);
if ($authResp !== null) { if ($authResp !== null) {
return $authResp; return $authResp;
} }
$credential = $this->entityManager->getRepository(S3Credential::class)->find($id); $credential = $this->entityManager->getRepository(S3Credential::class)->find($id);
if (!$credential) { if (!$credential) {
return new JsonResponse(['error' => 'Credential not found'], 404); return new JsonResponse(['error' => 'Credential not found'], 404);
} }
if ($request->getMethod() === 'GET') { return new JsonResponse([
return new JsonResponse([ 'id' => $credential->getId(),
'id' => $credential->getId(), 'access_key' => $credential->getAccessKey(),
'access_key' => $credential->getAccessKey(), 'secret_key' => $credential->getSecretKey(),
'secret_key' => $credential->getSecretKey(), 'user_name' => $credential->getUserName(),
'user_name' => $credential->getUserName(), 'is_active' => $credential->isActive(),
'is_active' => $credential->isActive(), 'created_at' => $credential->getCreatedAt()->format('Y-m-d H:i:s'),
'created_at' => $credential->getCreatedAt()->format('Y-m-d H:i:s'), 'buckets' => array_map(function($bucket) {
'buckets' => array_map(function($bucket) { return [
return [ 'name' => $bucket->getName(),
'name' => $bucket->getName(), 'region' => $bucket->getRegion(),
'region' => $bucket->getRegion(), 'created_at' => $bucket->getCreatedAt()->format('Y-m-d H:i:s')
'created_at' => $bucket->getCreatedAt()->format('Y-m-d H:i:s') ];
]; }, $credential->getBuckets()->toArray())
}, $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);
} }
// Buckets Management public function updateCredential(int $id, Request $request): JsonResponse
public function buckets(Request $request): JsonResponse
{ {
$authResp = $this->checkAuth($request); $authResp = $this->checkAuth($request);
if ($authResp !== null) { if ($authResp !== null) {
return $authResp; return $authResp;
} }
if ($request->getMethod() === 'GET') { $credential = $this->entityManager->getRepository(S3Credential::class)->find($id);
$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 [ if (!$credential) {
'name' => $bucket->getName(), return new JsonResponse(['error' => 'Credential not found'], 404);
'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 ($request->getMethod() === 'POST') { $data = json_decode($request->getContent(), true);
$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) { if (isset($data['user_name'])) {
return new JsonResponse(['error' => 'Missing bucket name or owner'], 400); $credential->setUserName($data['user_name']);
} }
if (isset($data['is_active'])) {
$credential->setIsActive($data['is_active']);
}
$owner = $this->entityManager->getRepository(S3Credential::class)->find($ownerId); $this->entityManager->flush();
if (!$owner) {
return new JsonResponse(['error' => 'Owner not found'], 404);
}
try { return new JsonResponse(['message' => 'Credential updated']);
$bucket = $this->s3Service->createBucket($bucketName, $owner, $region); }
return new JsonResponse([ 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(), 'name' => $bucket->getName(),
'region' => $bucket->getRegion(), 'region' => $bucket->getRegion(),
'owner' => $bucket->getOwner()->getUserName() ?: $bucket->getOwner()->getAccessKey(), 'owner' => $bucket->getOwner()->getUserName() ?: $bucket->getOwner()->getAccessKey(),
'created_at' => $bucket->getCreatedAt()->format('Y-m-d H:i:s') 'created_at' => $bucket->getCreatedAt()->format('Y-m-d H:i:s'),
], 201); 'object_count' => $objectCount,
} catch (\Exception $e) { 'total_size' => $totalSize,
return new JsonResponse(['error' => $e->getMessage()], 400); 'total_size_human' => $this->formatBytes($totalSize)
} ];
} }, $buckets)
]);
return new JsonResponse(['error' => 'Method not allowed'], 405);
} }
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); $authResp = $this->checkAuth($request);
if ($authResp !== null) { if ($authResp !== null) {
return $authResp; return $authResp;
} }
$bucket = $this->s3Service->findBucketByName($name); $bucket = $this->s3Service->findBucketByName($name);
if (!$bucket) { if (!$bucket) {
return new JsonResponse(['error' => 'Bucket not found'], 404); return new JsonResponse(['error' => 'Bucket not found'], 404);
} }
if ($request->getMethod() === 'GET') { $objects = $this->s3Service->listObjects($bucket);
$objects = $this->s3Service->listObjects($bucket); $totalSize = array_sum(array_map(fn($obj) => $obj->getSize(), $objects));
$totalSize = array_sum(array_map(fn($obj) => $obj->getSize(), $objects));
return new JsonResponse([ return new JsonResponse([
'name' => $bucket->getName(), 'name' => $bucket->getName(),
'region' => $bucket->getRegion(), 'region' => $bucket->getRegion(),
'owner' => $bucket->getOwner()->getUserName() ?: $bucket->getOwner()->getAccessKey(), 'owner' => $bucket->getOwner()->getUserName() ?: $bucket->getOwner()->getAccessKey(),
'created_at' => $bucket->getCreatedAt()->format('Y-m-d H:i:s'), 'created_at' => $bucket->getCreatedAt()->format('Y-m-d H:i:s'),
'object_count' => count($objects), 'object_count' => count($objects),
'total_size' => $totalSize, 'total_size' => $totalSize,
'total_size_human' => $this->formatBytes($totalSize), 'total_size_human' => $this->formatBytes($totalSize),
'objects' => array_map(function($obj) { 'objects' => array_map(function($obj) {
return [ return [
'key' => $obj->getObjectKey(), 'key' => $obj->getObjectKey(),
'size' => $obj->getSize(), 'size' => $obj->getSize(),
'size_human' => $this->formatBytes($obj->getSize()), 'size_human' => $this->formatBytes($obj->getSize()),
'content_type' => $obj->getContentType(), 'content_type' => $obj->getContentType(),
'etag' => $obj->getEtag(), 'etag' => $obj->getEtag(),
'is_multipart' => $obj->isMultipart(), 'is_multipart' => $obj->isMultipart(),
'created_at' => $obj->getCreatedAt()->format('Y-m-d H:i:s') 'created_at' => $obj->getCreatedAt()->format('Y-m-d H:i:s')
]; ];
}, $objects) }, $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 {
try { $this->s3Service->deleteBucket($bucket);
$this->s3Service->deleteBucket($bucket); return new JsonResponse(['message' => 'Bucket deleted']);
return new JsonResponse(['message' => 'Bucket deleted']); } catch (\Exception $e) {
} catch (\Exception $e) { return new JsonResponse(['error' => $e->getMessage()], 400);
return new JsonResponse(['error' => $e->getMessage()], 400);
}
} }
return new JsonResponse(['error' => 'Method not allowed'], 405);
} }
// Objects Management // Objects Management
public function objects(string $bucketName, Request $request): JsonResponse public function listObjects(string $bucketName, Request $request): JsonResponse
{ {
$authResp = $this->checkAuth($request); $authResp = $this->checkAuth($request);
if ($authResp !== null) { if ($authResp !== null) {
return $authResp; return $authResp;
} }
$bucket = $this->s3Service->findBucketByName($bucketName); $bucket = $this->s3Service->findBucketByName($bucketName);
if (!$bucket) { if (!$bucket) {
return new JsonResponse(['error' => 'Bucket not found'], 404); return new JsonResponse(['error' => 'Bucket not found'], 404);
} }
if ($request->getMethod() === 'GET') { $prefix = $request->query->get('prefix', '');
$prefix = $request->query->get('prefix', ''); $objects = $this->s3Service->listObjects($bucket, $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)
]);
}
if ($request->getMethod() === 'POST') { return new JsonResponse([
$objectKey = $request->headers->get('X-Object-Key') 'objects' => array_map(function($obj) {
?? $request->query->get('key') return [
?? $request->request->get('object_key'); 'key' => $obj->getObjectKey(),
'size' => $obj->getSize(),
if (empty($objectKey)) { 'size_human' => $this->formatBytes($obj->getSize()),
return new JsonResponse(['error' => 'Missing object key'], 400); 'content_type' => $obj->getContentType(),
} 'etag' => $obj->getEtag(),
'is_multipart' => $obj->isMultipart(),
$contentType = $request->headers->get('Content-Type', 'application/octet-stream'); 'created_at' => $obj->getCreatedAt()->format('Y-m-d H:i:s')
$file = $request->files->get('file'); ];
if ($file instanceof \Symfony\Component\HttpFoundation\File\UploadedFile) { }, $objects)
$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);
} }
public function objectDetail(string $bucketName, string $objectKey, Request $request): JsonResponse public function createObject(string $bucketName, Request $request): JsonResponse
{ {
$authResp = $this->checkAuth($request); $authResp = $this->checkAuth($request);
if ($authResp !== null) { if ($authResp !== null) {
return $authResp; return $authResp;
} }
$bucket = $this->s3Service->findBucketByName($bucketName); $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) { if (!$bucket) {
return new JsonResponse(['error' => 'Bucket not found'], 404); return new JsonResponse(['error' => 'Bucket not found'], 404);
} }
$object = $this->s3Service->findObject($bucket, $objectKey); $object = $this->s3Service->findObject($bucket, $objectKey);
if (!$object) { if (!$object) {
return new JsonResponse(['error' => 'Object not found'], 404); return new JsonResponse(['error' => 'Object not found'], 404);
} }
if ($request->getMethod() === 'GET') { return new JsonResponse([
return new JsonResponse([ 'key' => $object->getObjectKey(),
'key' => $object->getObjectKey(), 'size' => $object->getSize(),
'size' => $object->getSize(), 'size_human' => $this->formatBytes($object->getSize()),
'size_human' => $this->formatBytes($object->getSize()), 'content_type' => $object->getContentType(),
'content_type' => $object->getContentType(), 'etag' => $object->getEtag(),
'etag' => $object->getEtag(), 'is_multipart' => $object->isMultipart(),
'is_multipart' => $object->isMultipart(), 'part_count' => $object->getPartCount(),
'part_count' => $object->getPartCount(), 'metadata' => $object->getMetadata(),
'metadata' => $object->getMetadata(), 'storage_path' => $object->getStoragePath(),
'storage_path' => $object->getStoragePath(), 'created_at' => $object->getCreatedAt()->format('Y-m-d H:i:s'),
'created_at' => $object->getCreatedAt()->format('Y-m-d H:i:s'), 'updated_at' => $object->getUpdatedAt()->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') { $object = $this->s3Service->findObject($bucket, $objectKey);
$this->s3Service->deleteObject($object);
return new JsonResponse(['message' => 'Object deleted']); 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 public function createPresignedUpload(string $bucketName, string $objectKey, Request $request): JsonResponse
@@ -435,61 +497,64 @@ class ConsoleApiController extends AbstractController
} }
// Presigned URLs // Presigned URLs
public function presignedUrls(Request $request): JsonResponse public function listPresignedUrls(Request $request): JsonResponse
{ {
$authResp = $this->checkAuth($request); $authResp = $this->checkAuth($request);
if ($authResp !== null) { if ($authResp !== null) {
return $authResp; 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 = $this->entityManager->getRepository(\App\Entity\S3PresignedUrl::class)
'urls' => array_map(function($url) { ->createQueryBuilder('p')
return [ ->where('p.expiresAt > :now')
'bucket_name' => $url->getBucketName(), ->setParameter('now', new \DateTime())
'object_key' => $url->getObjectKey(), ->orderBy('p.createdAt', 'DESC')
'method' => $url->getMethod(), ->setMaxResults(100)
'access_key' => $url->getAccessKey(), ->getQuery()
'expires_at' => $url->getExpiresAt()->format('Y-m-d H:i:s'), ->getResult();
'created_at' => $url->getCreatedAt()->format('Y-m-d H:i:s'),
'url' => $url->getUrl() return new JsonResponse([
]; 'urls' => array_map(function($url) {
}, $urls) 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);
$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;
if (!$bucketName || !$objectKey || !$accessKey) { $bucketName = $data['bucket_name'] ?? null;
return new JsonResponse(['error' => 'Missing required fields'], 400); $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 (!$bucketName || !$objectKey || !$accessKey) {
if (!$credential) { return new JsonResponse(['error' => 'Missing required fields'], 400);
return new JsonResponse(['error' => 'Invalid access key'], 404);
}
$url = $this->s3Service->generatePresignedUrl($bucketName, $objectKey, $credential, $method, $expiresIn);
return new JsonResponse(['url' => $url], 201);
} }
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 // Statistics

View File

@@ -18,28 +18,32 @@ class ConsoleController extends AbstractController
return $this->render('console/index.html.twig'); 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')) { if ($request->getSession()->get('console_logged_in')) {
return new RedirectResponse('/console'); return new RedirectResponse('/console');
} }
$error = null; return $this->render('console/login.html.twig', ['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';
if ($user === $envUser && $pass === $envPass) { public function loginSubmit(Request $request): Response
$request->getSession()->set('console_logged_in', true); {
return new RedirectResponse('/console'); if ($request->getSession()->get('console_logged_in')) {
} return new RedirectResponse('/console');
$error = 'Invalid credentials';
} }
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 public function logout(Request $request): Response