2025-06-05 09:17:47 -04:00
|
|
|
<?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
|
|
|
|
|
) {}
|
|
|
|
|
|
2025-06-05 09:56:25 -04:00
|
|
|
private function checkAuth(Request $request): ?JsonResponse
|
|
|
|
|
{
|
|
|
|
|
if (!$request->getSession()->get('console_logged_in')) {
|
|
|
|
|
return new JsonResponse(['error' => 'Unauthorized'], Response::HTTP_UNAUTHORIZED);
|
|
|
|
|
}
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-05 09:17:47 -04:00
|
|
|
// Credentials Management
|
|
|
|
|
public function credentials(Request $request): JsonResponse
|
|
|
|
|
{
|
2025-06-05 09:56:25 -04:00
|
|
|
if ($resp = $this->checkAuth($request)) {
|
|
|
|
|
return $resp;
|
|
|
|
|
}
|
2025-06-05 09:17:47 -04:00
|
|
|
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
|
|
|
|
|
{
|
2025-06-05 09:56:25 -04:00
|
|
|
if ($resp = $this->checkAuth($request)) {
|
|
|
|
|
return $resp;
|
|
|
|
|
}
|
2025-06-05 09:17:47 -04:00
|
|
|
$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
|
|
|
|
|
{
|
2025-06-05 09:56:25 -04:00
|
|
|
if ($resp = $this->checkAuth($request)) {
|
|
|
|
|
return $resp;
|
|
|
|
|
}
|
2025-06-05 09:17:47 -04:00
|
|
|
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;
|
2025-06-05 10:14:46 -04:00
|
|
|
$region = $data['region'] ?? ($_ENV['APP_REGION'] ?? 'us-east-1');
|
2025-06-05 09:17:47 -04:00
|
|
|
|
|
|
|
|
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
|
|
|
|
|
{
|
2025-06-05 09:56:25 -04:00
|
|
|
if ($resp = $this->checkAuth($request)) {
|
|
|
|
|
return $resp;
|
|
|
|
|
}
|
2025-06-05 09:17:47 -04:00
|
|
|
$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
|
|
|
|
|
{
|
2025-06-05 09:56:25 -04:00
|
|
|
if ($resp = $this->checkAuth($request)) {
|
|
|
|
|
return $resp;
|
|
|
|
|
}
|
2025-06-05 09:17:47 -04:00
|
|
|
$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
|
|
|
|
|
{
|
2025-06-05 09:56:25 -04:00
|
|
|
if ($resp = $this->checkAuth($request)) {
|
|
|
|
|
return $resp;
|
|
|
|
|
}
|
2025-06-05 09:17:47 -04:00
|
|
|
$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
|
|
|
|
|
{
|
2025-06-05 09:56:25 -04:00
|
|
|
if ($resp = $this->checkAuth($request)) {
|
|
|
|
|
return $resp;
|
|
|
|
|
}
|
2025-06-05 09:17:47 -04:00
|
|
|
$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
|
|
|
|
|
{
|
2025-06-05 09:56:25 -04:00
|
|
|
if ($resp = $this->checkAuth($request)) {
|
|
|
|
|
return $resp;
|
|
|
|
|
}
|
2025-06-05 09:17:47 -04:00
|
|
|
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'),
|
2025-06-05 10:07:14 -04:00
|
|
|
'created_at' => $url->getCreatedAt()->format('Y-m-d H:i:s'),
|
|
|
|
|
'url' => $url->getUrl()
|
2025-06-05 09:17:47 -04:00
|
|
|
];
|
|
|
|
|
}, $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);
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-05 10:07:14 -04:00
|
|
|
$url = $this->s3Service->generatePresignedUrl($bucketName, $objectKey, $credential, $method, $expiresIn);
|
2025-06-05 09:17:47 -04:00
|
|
|
|
|
|
|
|
return new JsonResponse(['url' => $url], 201);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return new JsonResponse(['error' => 'Method not allowed'], 405);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Statistics
|
|
|
|
|
public function stats(Request $request): JsonResponse
|
|
|
|
|
{
|
2025-06-05 09:56:25 -04:00
|
|
|
if ($resp = $this->checkAuth($request)) {
|
|
|
|
|
return $resp;
|
|
|
|
|
}
|
2025-06-05 09:17:47 -04:00
|
|
|
$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];
|
|
|
|
|
}
|
|
|
|
|
}
|