| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416 |
- <?php
- namespace App\Services;
- use Illuminate\Support\Facades\Storage;
- use Illuminate\Support\Facades\Log;
- use Illuminate\Support\Str;
- use Illuminate\Http\UploadedFile;
- class RecordFileService
- {
- /**
- * Get client name from session, fallback to 'default'
- */
- private function getClientName()
- {
- $clientName = session('clientName', 'default');
- $clientName = Str::slug($clientName, '_');
- Log::info("Using client name for folders: {$clientName}");
- return $clientName;
- }
- /**
- * Create record folders with client structure
- */
- public function createRecordFolders($recordId, $type)
- {
- $clientName = $this->getClientName();
- $type = strtolower($type);
- Log::info("Preparing S3 structure for client: {$clientName}, record {$recordId}, type: {$type}");
- $folderPath = "{$clientName}/records/{$type}/{$recordId}/attachments";
- Log::info("S3 folder structure: {$folderPath}");
- return true;
- }
- /**
- * Store file temporarily with client structure
- */
- public function storeTemporarily($uploadedFile)
- {
- try {
- $clientName = $this->getClientName();
- $extension = $uploadedFile->getClientOriginalExtension();
- $fileName = time() . '_' . Str::random(10) . '.' . $extension;
- $tempPath = "{$clientName}/temp/uploads/{$fileName}";
- Log::info("=== STORING FILE TEMPORARILY ===");
- Log::info("Client: {$clientName}");
- Log::info("Original filename: " . $uploadedFile->getClientOriginalName());
- Log::info("File size: " . $uploadedFile->getSize() . " bytes");
- Log::info("Temp path: {$tempPath}");
- try {
- $storedPath = Storage::disk('s3')->putFileAs("{$clientName}/temp/uploads", $uploadedFile, $fileName);
- Log::info("Method 1 success - putFileAs returned: {$storedPath}");
- if (Storage::disk('s3')->exists($tempPath)) {
- $storedSize = Storage::disk('s3')->size($tempPath);
- Log::info("File verification successful - size: {$storedSize} bytes");
- if ($storedSize === $uploadedFile->getSize()) {
- Log::info("File sizes match perfectly");
- return $tempPath;
- } else {
- Log::warning("⚠ File size mismatch - Original: {$uploadedFile->getSize()}, Stored: {$storedSize}");
- return $tempPath;
- }
- } else {
- throw new \Exception("File not found after putFileAs");
- }
- } catch (\Exception $e) {
- Log::warning("Method 1 failed: " . $e->getMessage());
- }
- try {
- Log::info("Trying Method 2: put with file contents");
- $fileContent = file_get_contents($uploadedFile->getRealPath());
- if (!$fileContent) {
- throw new \Exception("Could not read file contents");
- }
- $stored = Storage::disk('s3')->put($tempPath, $fileContent);
- if ($stored && Storage::disk('s3')->exists($tempPath)) {
- Log::info("Method 2 success - put with contents");
- return $tempPath;
- } else {
- throw new \Exception("Put method failed");
- }
- } catch (\Exception $e) {
- Log::warning("Method 2 failed: " . $e->getMessage());
- }
- throw new \Exception("All temp storage methods failed");
- } catch (\Exception $e) {
- Log::error("Error storing file temporarily: " . $e->getMessage());
- Log::error("Stack trace: " . $e->getTraceAsString());
- throw $e;
- }
- }
- /**
- * Upload attachment directly to final S3 location with client structure
- */
- public function uploadAttachment($file, $recordId, $type)
- {
- try {
- $clientName = $this->getClientName();
- $type = strtolower($type);
- $extension = $file->getClientOriginalExtension();
- $fileName = time() . '_' . Str::random(10) . '.' . $extension;
- $finalPath = "{$clientName}/records/{$type}/{$recordId}/attachments/{$fileName}";
- Log::info("Uploading attachment to S3:");
- Log::info("- Client: {$clientName}");
- Log::info("- Record ID: {$recordId}");
- Log::info("- Type: {$type}");
- Log::info("- File path: {$finalPath}");
- Log::info("- File size: " . $file->getSize() . " bytes");
- $storedPath = Storage::disk('s3')->putFileAs(
- "{$clientName}/records/{$type}/{$recordId}/attachments",
- $file,
- $fileName
- );
- Log::info("File uploaded successfully to S3: {$storedPath}");
- if (Storage::disk('s3')->exists($finalPath)) {
- Log::info("S3 upload verified successfully");
- return $finalPath;
- } else {
- throw new \Exception("File verification failed - not found on S3");
- }
- } catch (\Exception $e) {
- Log::error("Error uploading attachment to S3: " . $e->getMessage());
- throw $e;
- }
- }
- /**
- * Upload XML receipt for import functionality with client structure
- */
- public function uploadXmlReceipt($file, $recordId, $type)
- {
- try {
- $clientName = $this->getClientName();
- $type = strtolower($type);
- $extension = $file->getClientOriginalExtension() ?: 'xml';
- $fileName = 'receipt_' . time() . '_' . Str::random(8) . '.' . $extension;
- $finalPath = "{$clientName}/records/{$type}/{$recordId}/attachments/{$fileName}";
- Log::info("Uploading XML receipt to S3:");
- Log::info("- Client: {$clientName}");
- Log::info("- Path: {$finalPath}");
- $storedPath = Storage::disk('s3')->putFileAs(
- "{$clientName}/records/{$type}/{$recordId}/attachments",
- $file,
- $fileName
- );
- Log::info("XML receipt uploaded to S3: {$storedPath}");
- return $finalPath;
- } catch (\Exception $e) {
- Log::error("Error uploading XML receipt to S3: " . $e->getMessage());
- throw $e;
- }
- }
- /**
- * Get S3 attachment URL
- */
- public function getAttachmentUrl($filePath)
- {
- try {
- if (!$filePath) {
- return null;
- }
- Log::info("Getting S3 attachment URL for: {$filePath}");
- if (!Storage::disk('s3')->exists($filePath)) {
- Log::warning("S3 attachment file not found: {$filePath}");
- $directory = dirname($filePath);
- try {
- $files = Storage::disk('s3')->files($directory);
- Log::info("Files in S3 directory {$directory}: " . json_encode($files));
- } catch (\Exception $e) {
- Log::warning("Could not list S3 directory {$directory}: " . $e->getMessage());
- }
- return null;
- }
- $url = Storage::disk('s3')->temporaryUrl($filePath, now()->addHours(1));
- Log::info("Generated S3 temporary URL for: {$filePath}");
- return $url;
- } catch (\Exception $e) {
- Log::error("Error getting S3 attachment URL for {$filePath}: " . $e->getMessage());
- return null;
- }
- }
- /**
- * Delete attachment from S3
- */
- public function deleteAttachment($filePath)
- {
- try {
- if (!$filePath) {
- return false;
- }
- Log::info("Deleting S3 attachment: {$filePath}");
- if (Storage::disk('s3')->exists($filePath)) {
- $deleted = Storage::disk('s3')->delete($filePath);
- if ($deleted) {
- Log::info("S3 attachment deleted successfully: {$filePath}");
- return true;
- } else {
- Log::error("Failed to delete S3 attachment: {$filePath}");
- return false;
- }
- } else {
- Log::warning("S3 attachment not found for deletion: {$filePath}");
- return false;
- }
- } catch (\Exception $e) {
- Log::error("Error deleting S3 attachment: " . $e->getMessage());
- throw $e;
- }
- }
- /**
- * Debug S3 configuration and connectivity
- */
- public function debugFileSystem()
- {
- $clientName = $this->getClientName();
- Log::info("=== S3 DEBUG ===");
- Log::info("Client Name: {$clientName}");
- Log::info("S3 Configuration:");
- Log::info("- Bucket: " . config('filesystems.disks.s3.bucket'));
- Log::info("- Region: " . config('filesystems.disks.s3.region'));
- Log::info("- URL: " . config('filesystems.disks.s3.url'));
- Log::info("- Key: " . (config('filesystems.disks.s3.key') ? 'Set' : 'Not set'));
- Log::info("- Secret: " . (config('filesystems.disks.s3.secret') ? 'Set' : 'Not set'));
- try {
- $testFile = "{$clientName}/test_connection_" . time() . '.txt';
- $testContent = 'S3 connection test: ' . now();
- Log::info("Testing S3 connection with client structure...");
- Storage::disk('s3')->put($testFile, $testContent);
- if (Storage::disk('s3')->exists($testFile)) {
- Log::info("S3 connection test: SUCCESS");
- Storage::disk('s3')->delete($testFile);
- } else {
- Log::error("S3 connection test: FAILED - file not found after upload");
- }
- } catch (\Exception $e) {
- Log::error("S3 connection test: FAILED - " . $e->getMessage());
- }
- Log::info("=== END S3 DEBUG ===");
- }
- /**
- * Clean up old temp files from S3 for specific client or all clients
- */
- public function cleanupTempFiles($olderThanHours = 24, $specificClient = null)
- {
- try {
- $clientName = $specificClient ?: $this->getClientName();
- $tempPath = $specificClient ? "{$specificClient}/temp/uploads" : "{$clientName}/temp/uploads";
- $tempFiles = Storage::disk('s3')->files($tempPath);
- $cutoffTime = now()->subHours($olderThanHours);
- $deletedCount = 0;
- Log::info("Cleaning up S3 temp files for client '{$clientName}' older than {$olderThanHours} hours");
- foreach ($tempFiles as $file) {
- $fileTime = Storage::disk('s3')->lastModified($file);
- if ($fileTime < $cutoffTime->timestamp) {
- if (Storage::disk('s3')->delete($file)) {
- $deletedCount++;
- Log::info("Cleaned up old S3 temp file: {$file}");
- }
- }
- }
- Log::info("S3 cleanup completed for client '{$clientName}'. Deleted {$deletedCount} temp files.");
- } catch (\Exception $e) {
- Log::error("Error cleaning up S3 temp files: " . $e->getMessage());
- }
- }
- /**
- * Clean up temp files for all clients
- */
- public function cleanupAllClientTempFiles($olderThanHours = 24)
- {
- try {
- $allDirectories = Storage::disk('s3')->directories('');
- $deletedCount = 0;
- Log::info("Cleaning up temp files for all clients older than {$olderThanHours} hours");
- foreach ($allDirectories as $clientDir) {
- $tempPath = "{$clientDir}/temp/uploads";
- if (Storage::disk('s3')->exists($tempPath)) {
- $tempFiles = Storage::disk('s3')->files($tempPath);
- $cutoffTime = now()->subHours($olderThanHours);
- foreach ($tempFiles as $file) {
- $fileTime = Storage::disk('s3')->lastModified($file);
- if ($fileTime < $cutoffTime->timestamp) {
- if (Storage::disk('s3')->delete($file)) {
- $deletedCount++;
- Log::info("Cleaned up old S3 temp file: {$file}");
- }
- }
- }
- }
- }
- Log::info("S3 cleanup completed for all clients. Deleted {$deletedCount} temp files.");
- } catch (\Exception $e) {
- Log::error("Error cleaning up all client temp files: " . $e->getMessage());
- }
- }
- /**
- * Check if a file exists on S3
- */
- public function fileExists($filePath)
- {
- try {
- return Storage::disk('s3')->exists($filePath);
- } catch (\Exception $e) {
- Log::error("Error checking if S3 file exists: " . $e->getMessage());
- return false;
- }
- }
- /**
- * Get file size from S3
- */
- public function getFileSize($filePath)
- {
- try {
- if (Storage::disk('s3')->exists($filePath)) {
- return Storage::disk('s3')->size($filePath);
- }
- return 0;
- } catch (\Exception $e) {
- Log::error("Error getting S3 file size: " . $e->getMessage());
- return 0;
- }
- }
- /**
- * Get file last modified time from S3
- */
- public function getFileLastModified($filePath)
- {
- try {
- if (Storage::disk('s3')->exists($filePath)) {
- return Storage::disk('s3')->lastModified($filePath);
- }
- return null;
- } catch (\Exception $e) {
- Log::error("Error getting S3 file last modified: " . $e->getMessage());
- return null;
- }
- }
- /**
- * Get all files for a specific client and type
- */
- public function getClientFiles($type = null, $clientName = null)
- {
- try {
- $clientName = $clientName ?: $this->getClientName();
- $type = $type ? strtolower($type) : '*';
- $basePath = $type === '*' ? "{$clientName}/records" : "{$clientName}/records/{$type}";
- $files = Storage::disk('s3')->allFiles($basePath);
- Log::info("Found " . count($files) . " files for client '{$clientName}' and type '{$type}'");
- return $files;
- } catch (\Exception $e) {
- Log::error("Error getting client files: " . $e->getMessage());
- return [];
- }
- }
- }
|