myCRM/src/Controller/DocumentController.php
olli 3c36fdd9a1 feat: Add Document and GitRepository entities with repositories and services
- Created Document entity with properties for file management, including filename, originalFilename, mimeType, size, uploadedAt, uploadedBy, relatedEntity, relatedEntityId, and description.
- Implemented DocumentRepository for querying documents by related entity and user.
- Added GitRepository entity with properties for managing Git repositories, including URL, localPath, branch, provider, accessToken, project, lastSync, name, description, createdAt, and updatedAt.
- Implemented GitRepositoryRepository for querying repositories by project.
- Developed GitHubService for interacting with GitHub API, including methods for fetching commits, contributions, branches, and repository info.
- Developed GitService for local Git repository interactions, including methods for fetching commits, contributions, branches, and repository info.
- Developed GiteaService for interacting with Gitea API, including methods for fetching commits, contributions, branches, repository info, and testing connection.
2025-11-12 16:14:18 +01:00

160 lines
5.8 KiB
PHP

<?php
namespace App\Controller;
use App\Entity\Document;
use App\Entity\User;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\File\Exception\FileException;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\BinaryFileResponse;
use Symfony\Component\HttpFoundation\ResponseHeaderBag;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\String\Slugger\SluggerInterface;
#[Route('/api/documents')]
class DocumentController extends AbstractController
{
private string $uploadsDirectory;
public function __construct(
private EntityManagerInterface $entityManager,
private SluggerInterface $slugger
) {
// Get project directory from kernel parameter
$this->uploadsDirectory = '%kernel.project_dir%/var/uploads/documents';
}
#[Route('/upload', name: 'api_document_upload', methods: ['POST'])]
public function upload(Request $request): JsonResponse
{
$this->denyAccessUnlessGranted('IS_AUTHENTICATED_FULLY');
// Resolve the actual path
$uploadsDirectory = str_replace('%kernel.project_dir%', $this->getParameter('kernel.project_dir'), $this->uploadsDirectory);
// Erstelle Upload-Verzeichnis falls nicht vorhanden
if (!is_dir($uploadsDirectory)) {
mkdir($uploadsDirectory, 0755, true);
}
/** @var UploadedFile $file */
$file = $request->files->get('file');
if (!$file) {
return $this->json(['error' => 'Keine Datei hochgeladen'], 400);
}
// Validierung
$maxSize = 10 * 1024 * 1024; // 10MB
if ($file->getSize() > $maxSize) {
return $this->json(['error' => 'Datei ist zu groß (max. 10MB)'], 400);
}
// Erlaubte Dateitypen
$allowedMimeTypes = [
// Bilder
'image/jpeg',
'image/jpg',
'image/png',
'image/gif',
'image/webp',
'image/svg+xml',
// PDF
'application/pdf',
// Excel
'application/vnd.ms-excel',
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
// CSV
'text/csv',
'text/plain',
// Word
'application/msword',
'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
];
$mimeType = $file->getClientMimeType();
if (!in_array($mimeType, $allowedMimeTypes)) {
return $this->json([
'error' => 'Dateityp nicht erlaubt. Erlaubt sind: Bilder (JPG, PNG, GIF, WebP, SVG), PDF, Excel (XLS, XLSX), CSV und Word (DOC, DOCX)'
], 400);
}
$relatedEntity = $request->request->get('relatedEntity');
$relatedEntityId = $request->request->get('relatedEntityId');
if (!$relatedEntity || !$relatedEntityId) {
return $this->json(['error' => 'relatedEntity und relatedEntityId sind erforderlich'], 400);
}
// Datei-Informationen VOR dem Move speichern
$originalFilename = $file->getClientOriginalName();
$mimeType = $file->getClientMimeType();
$fileSize = $file->getSize();
// Sichere Dateinamen generieren
$safeFilename = $this->slugger->slug(pathinfo($originalFilename, PATHINFO_FILENAME));
$newFilename = $safeFilename . '-' . uniqid() . '.' . $file->guessExtension();
try {
$file->move($uploadsDirectory, $newFilename);
} catch (FileException $e) {
return $this->json(['error' => 'Datei konnte nicht gespeichert werden'], 500);
}
// Document Entity erstellen
$document = new Document();
$document->setFilename($newFilename);
$document->setOriginalFilename($originalFilename);
$document->setMimeType($mimeType);
$document->setSize($fileSize);
$document->setRelatedEntity($relatedEntity);
$document->setRelatedEntityId((int) $relatedEntityId);
$document->setDescription($request->request->get('description'));
/** @var User $user */
$user = $this->getUser();
$document->setUploadedBy($user);
$this->entityManager->persist($document);
$this->entityManager->flush();
return $this->json($document, 201, [], ['groups' => 'document:read']);
}
#[Route('/{id}/download', name: 'api_document_download', methods: ['GET'])]
public function download(Document $document): BinaryFileResponse
{
$this->denyAccessUnlessGranted('VIEW', $document);
$uploadsDirectory = str_replace('%kernel.project_dir%', $this->getParameter('kernel.project_dir'), $this->uploadsDirectory);
$filePath = $uploadsDirectory . '/' . $document->getFilename();
if (!file_exists($filePath)) {
throw $this->createNotFoundException('Datei nicht gefunden');
}
$response = new BinaryFileResponse($filePath);
// Inline anzeigen statt Download für gängige Dateitypen
$mimeType = $document->getMimeType();
$inlineTypes = ['application/pdf', 'image/jpeg', 'image/jpg', 'image/png', 'image/gif', 'image/webp'];
if (in_array($mimeType, $inlineTypes)) {
$response->setContentDisposition(
ResponseHeaderBag::DISPOSITION_INLINE,
$document->getOriginalFilename()
);
} else {
$response->setContentDisposition(
ResponseHeaderBag::DISPOSITION_ATTACHMENT,
$document->getOriginalFilename()
);
}
return $response;
}
}