{"id":31920220,"url":"https://github.com/iamgerwin/nova-media-hub","last_synced_at":"2025-10-13T21:56:31.479Z","repository":{"id":318731776,"uuid":"1075585217","full_name":"iamgerwin/nova-media-hub","owner":"iamgerwin","description":"A comprehensive media management package for Laravel Nova with advanced chunked upload support for handling large files efficiently.","archived":false,"fork":false,"pushed_at":"2025-10-13T18:20:06.000Z","size":0,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-10-13T18:56:54.090Z","etag":null,"topics":["filemanager","media","mediahub","nova"],"latest_commit_sha":null,"homepage":"","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/iamgerwin.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-10-13T17:45:06.000Z","updated_at":"2025-10-13T18:20:00.000Z","dependencies_parsed_at":"2025-10-13T18:57:26.392Z","dependency_job_id":null,"html_url":"https://github.com/iamgerwin/nova-media-hub","commit_stats":null,"previous_names":["iamgerwin/nova-media-hub"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/iamgerwin/nova-media-hub","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/iamgerwin%2Fnova-media-hub","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/iamgerwin%2Fnova-media-hub/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/iamgerwin%2Fnova-media-hub/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/iamgerwin%2Fnova-media-hub/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/iamgerwin","download_url":"https://codeload.github.com/iamgerwin/nova-media-hub/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/iamgerwin%2Fnova-media-hub/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":279016630,"owners_count":26085853,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-13T02:00:06.723Z","response_time":61,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["filemanager","media","mediahub","nova"],"created_at":"2025-10-13T21:56:27.453Z","updated_at":"2025-10-13T21:56:31.467Z","avatar_url":"https://github.com/iamgerwin.png","language":"PHP","readme":"# Nova Media Hub\n\n[![Tests](https://github.com/iamgerwin/nova-media-hub/workflows/Tests/badge.svg)](https://github.com/iamgerwin/nova-media-hub/actions)\n[![Latest Version](https://img.shields.io/packagist/v/iamgerwin/nova-media-hub.svg?style=flat-square)](https://packagist.org/packages/iamgerwin/nova-media-hub)\n[![License](https://img.shields.io/packagist/l/iamgerwin/nova-media-hub.svg?style=flat-square)](https://packagist.org/packages/iamgerwin/nova-media-hub)\n[![Total Downloads](https://img.shields.io/packagist/dt/iamgerwin/nova-media-hub.svg?style=flat-square)](https://packagist.org/packages/iamgerwin/nova-media-hub)\n\nA comprehensive media management package for [Laravel Nova](https://nova.laravel.com) with advanced chunked upload support for handling large files efficiently.\n\n## Table of Contents\n\n- [Features](#features)\n- [Requirements](#requirements)\n- [Package Structure](#package-structure)\n- [Installation](#installation)\n- [Configuration](#configuration)\n- [Usage](#usage)\n  - [Basic Usage](#basic-usage)\n  - [Field Configuration](#field-configuration)\n  - [Model Integration](#model-integration)\n  - [Chunked Upload API](#chunked-upload-api)\n- [Advanced Use Cases](#advanced-use-cases)\n- [Edge Cases](#edge-cases)\n- [Testing](#testing)\n- [Security](#security)\n- [Maintenance](#maintenance)\n- [Contributing](#contributing)\n- [Issues \u0026 Support](#issues--support)\n- [Credits](#credits)\n- [License](#license)\n\n## Features\n\n### Core Features\n\n- **Media Hub Interface** - Dedicated Nova tool for managing media assets\n- **Media Hub Field** - Custom field for selecting single or multiple media items\n- **Image Optimization** - Automatic optimization and multiple format conversions\n- **Collections** - Organize media into logical collections\n- **Dark Mode Support** - Full support for Nova's dark mode\n- **Localization** - Multi-language support with translation loader\n- **Custom Fields** - Extensible metadata fields (e.g., copyright, alt text)\n\n### Chunked Upload System\n\n- **Large File Support** - Upload files up to 1GB+ without server configuration changes\n- **Real-time Progress** - Visual progress tracking with percentage display\n- **Automatic Retry** - Intelligent retry logic for failed chunk uploads\n- **Smart Fallback** - Automatic fallback to standard upload for small files\n- **Memory Efficient** - Streaming-based chunk combination prevents memory exhaustion\n- **Session Management** - Cache-based upload state persistence\n- **Configurable** - Adjustable chunk sizes, retry attempts, and thresholds\n\n### Developer Features\n\n- **Multi-version Compatibility** - Supports PHP 8.2-8.3, Laravel 10-12, Nova 4-5\n- **Extensive Testing** - Comprehensive test suite with CI/CD pipeline\n- **Type Safety** - Full type hints and strict type checking\n- **Clean API** - Intuitive, well-documented API\n- **Extensible** - Hooks and events for customization\n\n## Requirements\n\n| Component | Version |\n|-----------|---------|\n| PHP | 8.2, 8.3+ |\n| Laravel | 10.x, 11.x, 12.x |\n| Laravel Nova | 4.x, 5.x |\n\n## Package Structure\n\n```\nnova-media-hub/\n├── .github/\n│   └── workflows/\n│       └── tests.yml                          # CI/CD pipeline\n├── config/\n│   └── nova-media-hub.php                     # Main configuration file\n├── database/\n│   └── migrations/\n│       ├── 2022_06_15_000000_create_media_library_table.php\n│       └── 2024_01_29_000000_add_nova_media_hub_indexes.php\n├── dist/                                      # Compiled assets\n│   ├── css/\n│   │   └── entry.css\n│   ├── js/\n│   │   ├── entry.js\n│   │   └── entry.js.LICENSE.txt\n│   └── mix-manifest.json\n├── docs/                                      # Documentation images\n│   ├── choose-media-dark.jpeg\n│   └── media-hub-dark.jpeg\n├── lang/                                      # Translation files\n│   ├── en.json\n│   ├── fa.json\n│   └── it.json\n├── resources/\n│   ├── css/\n│   │   └── entry.css                         # Source styles\n│   └── js/\n│       ├── api.js                            # API client\n│       ├── entry.js                          # Main entry point\n│       ├── components/                       # Vue components\n│       │   ├── ChunkedFileUpload.vue\n│       │   ├── DropZone.vue\n│       │   ├── MediaItem.vue\n│       │   ├── MediaItemContextMenu.vue\n│       │   ├── MediaOrderSelect.vue\n│       │   ├── MediaViewModalInfoListItem.vue\n│       │   ├── ModalFilterItem.vue\n│       │   └── PaginationLinks.vue\n│       ├── composables/                      # Vue composables\n│       │   └── useDragAndDrop.js\n│       ├── fields/                           # Nova field components\n│       │   └── MediaField/\n│       │       ├── DetailMediaHubField.vue\n│       │       ├── FormMediaHubField.vue\n│       │       └── IndexMediaHubField.vue\n│       ├── icons/                            # SVG icon components\n│       │   ├── AudioIcon.vue\n│       │   ├── CheckMarkIcon.vue\n│       │   ├── OtherIcon.vue\n│       │   └── VideoIcon.vue\n│       ├── mixins/                           # Vue mixins\n│       │   ├── HandlesMediaHubFieldValue.js\n│       │   ├── HandlesMediaLists.js\n│       │   └── HandlesMediaUpload.js\n│       ├── modals/                           # Modal components\n│       │   ├── ChooseMediaModal.vue\n│       │   ├── ConfirmDeleteModal.vue\n│       │   ├── MediaReplaceModal.vue\n│       │   ├── MediaUploadModal.vue\n│       │   ├── MediaViewModal.vue\n│       │   └── MoveToCollectionModal.vue\n│       ├── utils/                            # Utility classes\n│       │   └── ChunkedUploader.js           # Chunked upload handler\n│       └── views/                            # Main views\n│           └── NovaMediaHub.vue\n├── routes/\n│   └── api.php                               # API routes\n├── src/\n│   ├── Casts/\n│   │   └── MediaCast.php                     # Eloquent cast\n│   ├── Console/\n│   │   └── Commands/\n│   │       └── CleanupOldChunksCommand.php   # Cleanup command\n│   ├── Exceptions/                           # Custom exceptions\n│   │   ├── DiskDoesNotExistException.php\n│   │   ├── FileDoesNotExistException.php\n│   │   ├── FileTooLargeException.php\n│   │   ├── FileValidationException.php\n│   │   ├── MimeTypeNotAllowedException.php\n│   │   ├── NoFileProvidedException.php\n│   │   └── UnknownFileTypeException.php\n│   ├── Filters/                              # Nova filters\n│   │   ├── Collection.php\n│   │   ├── Search.php\n│   │   └── Sort.php\n│   ├── Http/\n│   │   ├── Controllers/\n│   │   │   ├── ChunkedMediaUploadController.php\n│   │   │   └── MediaHubController.php\n│   │   └── Middleware/\n│   │       └── Authorize.php\n│   ├── Jobs/\n│   │   └── MediaHubOptimizeAndConvertJob.php # Image optimization job\n│   ├── MediaHandler/\n│   │   ├── FileHandler.php                   # Main file handler\n│   │   └── Support/\n│   │       ├── Base64File.php\n│   │       ├── DatePathMaker.php\n│   │       ├── FileHelpers.php\n│   │       ├── FileNamer.php\n│   │       ├── FileValidator.php\n│   │       ├── Filesystem.php\n│   │       ├── MediaManipulator.php\n│   │       ├── MediaOptimizer.php\n│   │       ├── PathMaker.php\n│   │       ├── RemoteFile.php\n│   │       └── Traits/\n│   │           └── PathMakerHelpers.php\n│   ├── Models/\n│   │   └── Media.php                         # Media Eloquent model\n│   ├── Nova/\n│   │   ├── Fields/\n│   │   │   └── MediaHubField.php            # Nova field\n│   │   └── Resources/\n│   │       └── Media.php                     # Nova resource\n│   ├── MediaHub.php                          # Nova tool class\n│   └── MediaHubServiceProvider.php           # Service provider\n├── tests/                                     # Test suite\n│   ├── TestCase.php\n│   └── Unit/\n│       └── PackageTest.php\n├── workbench/                                 # Development workbench\n│   ├── app/\n│   ├── bootstrap/\n│   ├── database/\n│   ├── resources/\n│   └── routes/\n├── .editorconfig                              # Editor configuration\n├── .gitignore                                 # Git ignore rules\n├── .prettierrc                                # Prettier configuration\n├── CHANGELOG.md                               # Version history\n├── composer.json                              # PHP dependencies\n├── LICENSE.md                                 # MIT license\n├── package.json                               # NPM dependencies\n├── phpunit.xml                                # PHPUnit configuration\n├── README.md                                  # This file\n├── tailwind.config.js                         # Tailwind CSS config\n├── testbench.yaml                             # Testbench configuration\n└── webpack.mix.js                             # Laravel Mix config\n```\n\n## Installation\n\nInstall the package via Composer:\n\n```bash\ncomposer require iamgerwin/nova-media-hub\n```\n\nRun the migrations to create the media library table:\n\n```bash\nphp artisan migrate\n```\n\nPublish the configuration file (optional):\n\n```bash\nphp artisan vendor:publish --provider=\"Iamgerwin\\NovaMediaHub\\MediaHubServiceProvider\" --tag=\"config\"\n```\n\nPublish translations (optional):\n\n```bash\nphp artisan vendor:publish --provider=\"Iamgerwin\\NovaMediaHub\\MediaHubServiceProvider\" --tag=\"translations\"\n```\n\nRegister the tool in your `NovaServiceProvider`:\n\n```php\n// app/Providers/NovaServiceProvider.php\n\nuse Iamgerwin\\NovaMediaHub\\MediaHub;\n\npublic function tools()\n{\n    return [\n        MediaHub::make(),\n    ];\n}\n```\n\n## Configuration\n\nThe package configuration file is located at `config/nova-media-hub.php`. Key configuration options include:\n\n### Storage Configuration\n\n```php\n'disk' =\u003e env('MEDIA_HUB_DISK', 'public'),\n'path' =\u003e env('MEDIA_HUB_PATH', 'media'),\n```\n\n### Chunked Upload Configuration\n\n```php\n'chunked_upload' =\u003e [\n    // Enable/disable chunked upload feature\n    'enabled' =\u003e true,\n\n    // Size of each chunk (adjust based on server limits)\n    'chunk_size' =\u003e 5 * 1024 * 1024, // 5MB\n\n    // Maximum file size for uploads\n    'max_file_size' =\u003e 1024 * 1024 * 1024, // 1GB\n\n    // Upload session lifetime\n    'session_lifetime_hours' =\u003e 24,\n\n    // Retry attempts for failed chunks\n    'retry_attempts' =\u003e 3,\n\n    // Auto-enable chunked upload for files larger than this\n    'auto_threshold' =\u003e 10 * 1024 * 1024, // 10MB\n\n    // Cleanup old chunks after this many hours\n    'cleanup_old_chunks_after_hours' =\u003e 48,\n],\n```\n\n### Image Optimization\n\n```php\n'image_conversions' =\u003e [\n    'thumbnail' =\u003e [\n        'width' =\u003e 150,\n        'height' =\u003e 150,\n        'fit' =\u003e 'crop',\n    ],\n    'medium' =\u003e [\n        'width' =\u003e 800,\n        'height' =\u003e 600,\n        'fit' =\u003e 'contain',\n    ],\n],\n\n'image_optimizer' =\u003e [\n    'enabled' =\u003e true,\n    'quality' =\u003e 85,\n],\n```\n\n## Usage\n\n### Basic Usage\n\nAdd the `MediaHubField` to your Nova resource:\n\n```php\nuse Iamgerwin\\NovaMediaHub\\Nova\\Fields\\MediaHubField;\n\nclass Product extends Resource\n{\n    public function fields(Request $request)\n    {\n        return [\n            ID::make()-\u003esortable(),\n            Text::make('Name'),\n\n            MediaHubField::make('Image', 'image'),\n        ];\n    }\n}\n```\n\n### Field Configuration\n\n#### Single Media Selection\n\n```php\nMediaHubField::make('Featured Image', 'featured_image')\n    -\u003edefaultCollection('featured')\n    -\u003erules('required');\n```\n\n#### Multiple Media Selection\n\n```php\nMediaHubField::make('Gallery', 'gallery_images')\n    -\u003emultiple()\n    -\u003edefaultCollection('galleries')\n    -\u003emax(10);\n```\n\n#### Collection-specific Selection\n\n```php\nMediaHubField::make('Product Images', 'images')\n    -\u003emultiple()\n    -\u003edefaultCollection('products')\n    -\u003efilterCollection('products'); // Only show media from this collection\n```\n\n#### Hide from Listing\n\n```php\nMediaHubField::make('Images', 'images')\n    -\u003ehideFromIndex();\n```\n\n### Model Integration\n\n#### Using Media Cast\n\nThe package provides a custom cast for seamless model integration:\n\n```php\nuse Iamgerwin\\NovaMediaHub\\Casts\\MediaCast;\nuse Illuminate\\Database\\Eloquent\\Model;\n\nclass Product extends Model\n{\n    protected $casts = [\n        'image' =\u003e MediaCast::class,\n        'gallery_images' =\u003e MediaCast::class,\n    ];\n}\n```\n\n#### Accessing Media in Blade\n\n```blade\n{{-- Single media --}}\n\u003cimg src=\"{{ $product-\u003eimage-\u003eurl }}\" alt=\"{{ $product-\u003eimage-\u003ealt }}\"\u003e\n\n{{-- Multiple media --}}\n@foreach($product-\u003egallery_images as $image)\n    \u003cimg src=\"{{ $image-\u003eurl }}\" alt=\"{{ $image-\u003ealt }}\"\u003e\n@endforeach\n\n{{-- Specific conversion --}}\n\u003cimg src=\"{{ $product-\u003eimage-\u003eurl('thumbnail') }}\" alt=\"{{ $product-\u003eimage-\u003ealt }}\"\u003e\n```\n\n#### Accessing Media in Controllers\n\n```php\n// Get media URL\n$url = $product-\u003eimage-\u003eurl;\n$thumbnailUrl = $product-\u003eimage-\u003eurl('thumbnail');\n\n// Get media metadata\n$filename = $product-\u003eimage-\u003efilename;\n$mimeType = $product-\u003eimage-\u003emime_type;\n$size = $product-\u003eimage-\u003esize;\n$collection = $product-\u003eimage-\u003ecollection;\n\n// Check media type\n$isImage = $product-\u003eimage-\u003eisImage();\n$isVideo = $product-\u003eimage-\u003eisVideo();\n$isPdf = $product-\u003eimage-\u003eisPdf();\n```\n\n### Chunked Upload API\n\nFor custom implementations or advanced use cases, you can use the `ChunkedUploader` JavaScript class:\n\n```javascript\nimport { ChunkedUploader } from './utils/ChunkedUploader';\n\nconst uploader = new ChunkedUploader({\n  baseUrl: '/nova-vendor/media-hub/chunked',\n  chunkSize: 5 * 1024 * 1024, // 5MB\n  retryAttempts: 3,\n\n  onProgress: (chunkIndex, totalChunks, percentage) =\u003e {\n    console.log(`Uploading: ${percentage}%`);\n    updateProgressBar(percentage);\n  },\n\n  onComplete: (media) =\u003e {\n    console.log('Upload complete:', media);\n    displayMedia(media);\n  },\n\n  onError: (error) =\u003e {\n    console.error('Upload failed:', error);\n    showErrorMessage(error.message);\n  }\n});\n\n// Start upload\nconst file = document.getElementById('file-input').files[0];\nawait uploader.upload(file, 'my-collection', { alt: 'My Image' });\n\n// Cancel upload\nawait uploader.cancel();\n```\n\n## Advanced Use Cases\n\n### Custom Media Fields\n\nAdd custom metadata fields to the Media Hub:\n\n```php\n// app/Providers/NovaServiceProvider.php\n\nMediaHub::make()\n    -\u003ewithCustomFields([\n        'copyright' =\u003e __('Copyright'),\n        'photographer' =\u003e __('Photographer'),\n        'license' =\u003e __('License'),\n    ]);\n```\n\n### Programmatic Media Upload\n\n```php\nuse Iamgerwin\\NovaMediaHub\\MediaHandler\\FileHandler;\n\n$fileHandler = app(FileHandler::class);\n\n// Upload from file path\n$media = $fileHandler-\u003efromFile('/path/to/file.jpg', 'products', [\n    'alt' =\u003e 'Product image',\n    'copyright' =\u003e '© 2025',\n]);\n\n// Upload from URL\n$media = $fileHandler-\u003efromUrl('https://example.com/image.jpg', 'external', [\n    'alt' =\u003e 'External image',\n]);\n\n// Upload from base64\n$media = $fileHandler-\u003efromBase64($base64Data, 'uploads', [\n    'alt' =\u003e 'Base64 image',\n]);\n```\n\n### Batch Operations\n\n```php\nuse Iamgerwin\\NovaMediaHub\\Models\\Media;\n\n// Move media to another collection\nMedia::whereCollection('old-collection')\n    -\u003eupdate(['collection' =\u003e 'new-collection']);\n\n// Delete unused media\nMedia::whereDoesntHave('models')\n    -\u003ewhere('created_at', '\u003c', now()-\u003esubMonths(6))\n    -\u003edelete();\n\n// Get media by type\n$images = Media::where('mime_type', 'like', 'image/%')-\u003eget();\n$videos = Media::where('mime_type', 'like', 'video/%')-\u003eget();\n```\n\n### Custom Image Conversions\n\n```php\n// config/nova-media-hub.php\n\n'image_conversions' =\u003e [\n    'thumbnail' =\u003e [\n        'width' =\u003e 150,\n        'height' =\u003e 150,\n        'fit' =\u003e 'crop',\n    ],\n    'square' =\u003e [\n        'width' =\u003e 500,\n        'height' =\u003e 500,\n        'fit' =\u003e 'crop',\n    ],\n    'hero' =\u003e [\n        'width' =\u003e 1920,\n        'height' =\u003e 1080,\n        'fit' =\u003e 'contain',\n    ],\n],\n```\n\n### Queue Image Processing\n\nFor better performance, queue image optimization:\n\n```php\n// config/nova-media-hub.php\n\n'queue_conversions' =\u003e true,\n'queue_name' =\u003e 'media',\n```\n\nDon't forget to run the queue worker:\n\n```bash\nphp artisan queue:work --queue=media\n```\n\n## Edge Cases\n\n### Large File Uploads (\u003e100MB)\n\nFor very large files, adjust the chunk size and timeout settings:\n\n```php\n// config/nova-media-hub.php\n\n'chunked_upload' =\u003e [\n    'chunk_size' =\u003e 10 * 1024 * 1024, // 10MB chunks\n    'session_lifetime_hours' =\u003e 48, // Longer session\n    'retry_attempts' =\u003e 5, // More retry attempts\n],\n```\n\nAlso, ensure your server has adequate disk space for temporary chunks.\n\n### Slow Network Connections\n\nThe chunked upload system handles slow connections gracefully with automatic retries. Configure retry behavior:\n\n```php\n'chunked_upload' =\u003e [\n    'retry_attempts' =\u003e 5,\n    'retry_delay' =\u003e 2000, // milliseconds between retries\n],\n```\n\n### Concurrent Uploads\n\nThe package supports concurrent uploads using UUID-based session management. Each upload maintains its own isolated state.\n\n```javascript\n// Upload multiple files concurrently\nconst uploaders = files.map(file =\u003e {\n  const uploader = new ChunkedUploader(config);\n  return uploader.upload(file, 'collection');\n});\n\nawait Promise.all(uploaders);\n```\n\n### Memory-Constrained Environments\n\nThe package uses streaming for chunk combination to minimize memory usage:\n\n```php\n// Chunks are combined using stream_copy_to_stream()\n// Memory usage stays constant regardless of file size\n```\n\n### Interrupted Uploads\n\nIf an upload is interrupted (browser closed, connection lost), you can resume:\n\n```javascript\n// Check for existing upload session\nconst uploadId = localStorage.getItem('upload-session-id');\n\nif (uploadId) {\n  const status = await fetch(`/chunked/status/${uploadId}`).then(r =\u003e r.json());\n\n  if (status.chunks_uploaded \u003c status.total_chunks) {\n    // Resume from last uploaded chunk\n    uploader.resume(uploadId, status.chunks_uploaded);\n  }\n}\n```\n\n### Cross-Domain Uploads\n\nWhen uploading from a different domain, ensure CORS is properly configured:\n\n```php\n// config/cors.php\n\n'paths' =\u003e ['nova-vendor/media-hub/*'],\n'allowed_methods' =\u003e ['POST', 'GET', 'OPTIONS'],\n'allowed_origins' =\u003e ['https://your-domain.com'],\n'allowed_headers' =\u003e ['Content-Type', 'X-Requested-With', 'X-CSRF-TOKEN'],\n```\n\n### File Type Restrictions\n\nRestrict allowed file types:\n\n```php\n// config/nova-media-hub.php\n\n'allowed_mime_types' =\u003e [\n    'image/jpeg',\n    'image/png',\n    'image/webp',\n    'image/svg+xml',\n    'video/mp4',\n    'video/webm',\n    'application/pdf',\n],\n\n'max_file_size' =\u003e 50 * 1024 * 1024, // 50MB\n```\n\n## Testing\n\nThe package includes a comprehensive test suite to ensure stability and reliability.\n\n### Running Tests\n\n```bash\n# Run all tests\ncomposer test\n\n# Run tests with coverage\ncomposer test-coverage\n\n# Run specific test file\n./vendor/bin/phpunit tests/Unit/PackageTest.php\n\n# Run tests in verbose mode\n./vendor/bin/phpunit --verbose\n```\n\n### Test Structure\n\n```\ntests/\n├── TestCase.php              # Base test case\n└── Unit/\n    └── PackageTest.php       # Package loading tests\n```\n\n### Writing Tests\n\nWhen contributing, please include tests for new features:\n\n```php\nnamespace Iamgerwin\\NovaMediaHub\\Tests\\Unit;\n\nuse Iamgerwin\\NovaMediaHub\\Tests\\TestCase;\n\nclass YourFeatureTest extends TestCase\n{\n    /** @test */\n    public function it_does_something()\n    {\n        // Arrange\n        $input = 'test';\n\n        // Act\n        $result = yourFunction($input);\n\n        // Assert\n        $this-\u003eassertEquals('expected', $result);\n    }\n}\n```\n\n### Continuous Integration\n\nThe package uses GitHub Actions for automated testing across multiple PHP and Laravel versions:\n\n- **PHP Versions**: 8.2, 8.3\n- **Laravel Versions**: 10.x, 11.x\n- **Test Matrix**: 12+ version combinations\n\nView test results: [GitHub Actions](https://github.com/iamgerwin/nova-media-hub/actions)\n\n## Security\n\n### Security Vulnerabilities\n\nIf you discover a security vulnerability within Nova Media Hub, please send an email to [iamgerwin@live.com](mailto:iamgerwin@live.com). All security vulnerabilities will be promptly addressed.\n\n**Please do not create public GitHub issues for security vulnerabilities.**\n\n### Security Features\n\nThe package implements several security measures:\n\n#### File Upload Security\n\n- **MIME Type Validation** - Validates file types before upload\n- **File Size Limits** - Enforces maximum file size restrictions\n- **Filename Sanitization** - Removes dangerous characters from filenames\n- **Extension Whitelisting** - Only allows specified file extensions\n\n```php\n// config/nova-media-hub.php\n\n'allowed_mime_types' =\u003e [\n    'image/jpeg',\n    'image/png',\n    'image/webp',\n    'video/mp4',\n],\n\n'max_file_size' =\u003e 50 * 1024 * 1024, // 50MB\n```\n\n#### Chunked Upload Security\n\n- **UUID Session IDs** - Prevents session guessing attacks\n- **CSRF Protection** - All endpoints require valid CSRF tokens\n- **Chunk Index Validation** - Prevents malicious chunk injection\n- **File Hash Verification** - Validates file integrity after assembly\n- **Temporary File Isolation** - Chunks stored in isolated directories\n\n#### Authorization\n\n- **Nova Authorization** - Respects Nova's authorization policies\n- **Middleware Protection** - All routes protected by authentication middleware\n- **Permission Checks** - User permissions verified before operations\n\n```php\n// Define authorization in Nova resource\npublic static function authorizedToCreate(Request $request)\n{\n    return $request-\u003euser()-\u003ecan('create-media');\n}\n```\n\n### Best Practices\n\n1. **Validate User Input** - Always validate and sanitize user-provided data\n2. **Restrict File Types** - Only allow necessary MIME types\n3. **Set Size Limits** - Configure appropriate file size limits\n4. **Use HTTPS** - Always serve media over HTTPS in production\n5. **Regular Updates** - Keep the package and dependencies up to date\n6. **Monitor Uploads** - Log and monitor upload activity for suspicious patterns\n\n### Reported Vulnerabilities\n\nNone reported as of the latest release (0.0.3).\n\n## Maintenance\n\n### Cleanup Command\n\nThe package includes a cleanup command to remove old temporary chunk files:\n\n```bash\n# Run cleanup manually\nphp artisan media-hub:cleanup-chunks\n\n# Dry run (see what would be deleted)\nphp artisan media-hub:cleanup-chunks --dry-run\n\n# Custom time threshold (default: 48 hours)\nphp artisan media-hub:cleanup-chunks --hours=24\n```\n\n### Scheduled Cleanup\n\nAdd the cleanup command to your scheduler in `app/Console/Kernel.php`:\n\n```php\nprotected function schedule(Schedule $schedule)\n{\n    // Clean up chunks older than 48 hours, daily at 3am\n    $schedule-\u003ecommand('media-hub:cleanup-chunks')-\u003edailyAt('03:00');\n\n    // Or use cron expression\n    $schedule-\u003ecommand('media-hub:cleanup-chunks')-\u003ecron('0 3 * * *');\n}\n```\n\n### Monitoring Disk Usage\n\nMonitor storage disk usage to prevent issues:\n\n```bash\n# Check disk usage\ndf -h\n\n# Check media directory size\ndu -sh storage/app/public/media\n\n# Check chunks directory size\ndu -sh storage/app/chunks\n```\n\n## Contributing\n\nContributions are welcome! Please follow these guidelines:\n\n1. **Fork the repository**\n   ```bash\n   git clone https://github.com/iamgerwin/nova-media-hub.git\n   cd nova-media-hub\n   ```\n\n2. **Create a feature branch**\n   ```bash\n   git checkout -b feature/your-feature-name\n   ```\n\n3. **Install dependencies**\n   ```bash\n   composer install\n   npm install\n   ```\n\n4. **Make your changes**\n   - Write tests for new features\n   - Follow PSR-12 coding standards\n   - Update documentation as needed\n\n5. **Run tests**\n   ```bash\n   composer test\n   ./vendor/bin/pint\n   ```\n\n6. **Commit your changes**\n   ```bash\n   git add .\n   git commit -m \"Add feature: your feature description\"\n   ```\n\n7. **Push to your fork**\n   ```bash\n   git push origin feature/your-feature-name\n   ```\n\n8. **Create a Pull Request**\n   - Provide a clear description of the changes\n   - Reference any related issues\n   - Ensure CI tests pass\n\n### Development Setup\n\n```bash\n# Install dependencies\ncomposer install\n\n# Run tests\ncomposer test\n\n# Run tests with coverage\ncomposer test-coverage\n\n# Format code\n./vendor/bin/pint\n\n# Serve development environment\ncomposer serve\n```\n\n## Issues \u0026 Support\n\nFound a bug or have a feature request? Please create an issue on GitHub:\n\n**[Submit an Issue](https://github.com/iamgerwin/nova-media-hub/issues)**\n\nWhen submitting an issue, please include:\n\n- **Description** - Clear description of the issue or feature request\n- **Steps to Reproduce** - Detailed steps to reproduce the issue (for bugs)\n- **Expected Behavior** - What you expected to happen\n- **Actual Behavior** - What actually happened\n- **Environment**:\n  - PHP version\n  - Laravel version\n  - Nova version\n  - Package version\n  - Operating system\n- **Code Samples** - Relevant code snippets or configuration\n- **Screenshots** - If applicable\n\n### Before Submitting an Issue\n\n1. Check if the issue already exists\n2. Update to the latest version\n3. Check the [documentation](#table-of-contents)\n4. Review [closed issues](https://github.com/iamgerwin/nova-media-hub/issues?q=is%3Aissue+is%3Aclosed) for similar problems\n\n### Security Vulnerabilities\n\nIf you discover a security vulnerability, please email [iamgerwin@live.com](mailto:iamgerwin@live.com) instead of using the issue tracker.\n\n## Credits\n\n- **Author**: [John Gerwin De las Alas](https://github.com/iamgerwin)\n- **Contributors**: [All Contributors](https://github.com/iamgerwin/nova-media-hub/graphs/contributors)\n\n## License\n\nThe MIT License (MIT). Please see [License File](LICENSE.md) for more information.\n\n---\n\n**Links**\n\n- [Packagist](https://packagist.org/packages/iamgerwin/nova-media-hub)\n- [GitHub Repository](https://github.com/iamgerwin/nova-media-hub)\n- [Issue Tracker](https://github.com/iamgerwin/nova-media-hub/issues)\n- [Changelog](CHANGELOG.md)\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fiamgerwin%2Fnova-media-hub","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fiamgerwin%2Fnova-media-hub","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fiamgerwin%2Fnova-media-hub/lists"}