init commit
This commit is contained in:
434
src/Controller/ConsoleApiController.php
Normal file
434
src/Controller/ConsoleApiController.php
Normal file
@@ -0,0 +1,434 @@
|
||||
<?php
|
||||
|
||||
namespace App\Controller;
|
||||
|
||||
use App\Entity\S3Credential;
|
||||
use App\Entity\S3Bucket;
|
||||
use App\Entity\S3Object;
|
||||
use App\Service\S3Service;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
class ConsoleApiController extends AbstractController
|
||||
{
|
||||
public function __construct(
|
||||
private S3Service $s3Service,
|
||||
private EntityManagerInterface $entityManager
|
||||
) {}
|
||||
|
||||
// Credentials Management
|
||||
public function credentials(Request $request): JsonResponse
|
||||
{
|
||||
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;
|
||||
|
||||
$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);
|
||||
}
|
||||
|
||||
public function credentialDetail(int $id, Request $request): JsonResponse
|
||||
{
|
||||
$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);
|
||||
}
|
||||
|
||||
// Buckets Management
|
||||
public function buckets(Request $request): JsonResponse
|
||||
{
|
||||
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;
|
||||
|
||||
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 ($request->getMethod() === 'POST') {
|
||||
$data = json_decode($request->getContent(), true);
|
||||
|
||||
$bucketName = $data['name'] ?? null;
|
||||
$ownerId = $data['owner_id'] ?? null;
|
||||
$region = $data['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);
|
||||
}
|
||||
}
|
||||
|
||||
return new JsonResponse(['error' => 'Method not allowed'], 405);
|
||||
}
|
||||
|
||||
public function bucketDetail(string $name, Request $request): JsonResponse
|
||||
{
|
||||
$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));
|
||||
|
||||
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)
|
||||
]);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
return new JsonResponse(['error' => 'Method not allowed'], 405);
|
||||
}
|
||||
|
||||
// Objects Management
|
||||
public function objects(string $bucketName, Request $request): JsonResponse
|
||||
{
|
||||
$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)
|
||||
]);
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
$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')
|
||||
]);
|
||||
}
|
||||
|
||||
if ($request->getMethod() === 'DELETE') {
|
||||
$this->s3Service->deleteObject($object);
|
||||
return new JsonResponse(['message' => 'Object deleted']);
|
||||
}
|
||||
|
||||
return new JsonResponse(['error' => 'Method not allowed'], 405);
|
||||
}
|
||||
|
||||
// Multipart Uploads
|
||||
public function multipartUploads(string $bucketName, Request $request): JsonResponse
|
||||
{
|
||||
$bucket = $this->s3Service->findBucketByName($bucketName);
|
||||
|
||||
if (!$bucket) {
|
||||
return new JsonResponse(['error' => 'Bucket not found'], 404);
|
||||
}
|
||||
|
||||
$uploads = $this->s3Service->listMultipartUploads($bucket);
|
||||
|
||||
return new JsonResponse([
|
||||
'uploads' => array_map(function($upload) {
|
||||
$parts = $this->s3Service->listParts($upload);
|
||||
$totalSize = array_sum(array_map(fn($part) => $part->getSize(), $parts));
|
||||
|
||||
return [
|
||||
'upload_id' => $upload->getUploadId(),
|
||||
'object_key' => $upload->getObjectKey(),
|
||||
'initiated_by' => $upload->getInitiatedBy()->getUserName() ?: $upload->getInitiatedBy()->getAccessKey(),
|
||||
'content_type' => $upload->getContentType(),
|
||||
'initiated_at' => $upload->getInitiatedAt()->format('Y-m-d H:i:s'),
|
||||
'part_count' => count($parts),
|
||||
'total_size' => $totalSize,
|
||||
'total_size_human' => $this->formatBytes($totalSize)
|
||||
];
|
||||
}, $uploads)
|
||||
]);
|
||||
}
|
||||
|
||||
// Presigned URLs
|
||||
public function presignedUrls(Request $request): JsonResponse
|
||||
{
|
||||
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')
|
||||
];
|
||||
}, $urls)
|
||||
]);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
if (!$bucketName || !$objectKey || !$accessKey) {
|
||||
return new JsonResponse(['error' => 'Missing required fields'], 400);
|
||||
}
|
||||
|
||||
$credential = $this->s3Service->findCredentialByAccessKey($accessKey);
|
||||
if (!$credential) {
|
||||
return new JsonResponse(['error' => 'Invalid access key'], 404);
|
||||
}
|
||||
|
||||
$url = $this->s3Service->generatePresignedGetUrl($bucketName, $objectKey, $credential, $expiresIn);
|
||||
|
||||
return new JsonResponse(['url' => $url], 201);
|
||||
}
|
||||
|
||||
return new JsonResponse(['error' => 'Method not allowed'], 405);
|
||||
}
|
||||
|
||||
// Statistics
|
||||
public function stats(Request $request): JsonResponse
|
||||
{
|
||||
$credentialCount = $this->entityManager->getRepository(S3Credential::class)->count([]);
|
||||
$bucketCount = $this->entityManager->getRepository(S3Bucket::class)->count([]);
|
||||
$objectCount = $this->entityManager->getRepository(S3Object::class)->count([]);
|
||||
|
||||
$totalSize = $this->entityManager->createQueryBuilder()
|
||||
->select('SUM(o.size)')
|
||||
->from(S3Object::class, 'o')
|
||||
->getQuery()
|
||||
->getSingleScalarResult() ?: 0;
|
||||
|
||||
$multipartUploads = $this->entityManager->getRepository(\App\Entity\S3MultipartUpload::class)->count([]);
|
||||
$activePresignedUrls = $this->entityManager->getRepository(\App\Entity\S3PresignedUrl::class)
|
||||
->createQueryBuilder('p')
|
||||
->select('COUNT(p.id)')
|
||||
->where('p.expiresAt > :now')
|
||||
->setParameter('now', new \DateTime())
|
||||
->getQuery()
|
||||
->getSingleScalarResult();
|
||||
|
||||
return new JsonResponse([
|
||||
'credentials' => $credentialCount,
|
||||
'buckets' => $bucketCount,
|
||||
'objects' => $objectCount,
|
||||
'total_storage' => $totalSize,
|
||||
'total_storage_human' => $this->formatBytes($totalSize),
|
||||
'active_multipart_uploads' => $multipartUploads,
|
||||
'active_presigned_urls' => $activePresignedUrls
|
||||
]);
|
||||
}
|
||||
|
||||
private function formatBytes(int $bytes): string
|
||||
{
|
||||
if ($bytes === 0) return '0 B';
|
||||
|
||||
$units = ['B', 'KB', 'MB', 'GB', 'TB'];
|
||||
$i = floor(log($bytes, 1024));
|
||||
|
||||
return round($bytes / pow(1024, $i), 2) . ' ' . $units[$i];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user