ranitachi / filemanager-laravel
A secure, modular, and extensible File Manager for Laravel with RBAC, multi-storage, audit log, and WYSIWYG editor integration.
Requires
- php: ^8.0|^8.1|^8.2
- illuminate/cache: ^9.0|^10.0|^11.0
- illuminate/contracts: ^9.0|^10.0|^11.0
- illuminate/database: ^9.0|^10.0|^11.0
- illuminate/filesystem: ^9.0|^10.0|^11.0
- illuminate/http: ^9.0|^10.0|^11.0
- illuminate/queue: ^9.0|^10.0|^11.0
- illuminate/routing: ^9.0|^10.0|^11.0
- illuminate/support: ^9.0|^10.0|^11.0
Requires (Dev)
- fakerphp/faker: ^1.23
- mockery/mockery: ^1.6
- orchestra/testbench: ^8.0|^9.0
- pestphp/pest: ^2.0
- pestphp/pest-plugin-laravel: ^2.0
README
A secure, modular, and extensible File Manager package for Laravel 10/11.
Built as a drop-in replacement for unisharp/laravel-filemanager with enterprise-grade security, granular RBAC, multi-storage support, and native WYSIWYG editor integration.
✨ Features
| Feature | Details |
|---|---|
| 🔐 Security | Path traversal prevention, magic-byte MIME validation, signed URLs, ClamAV hook |
| 🗄 Multi-Storage | Local, S3, MinIO — swap via config, no code change |
| 👥 RBAC | Per-file / per-folder permissions for users and roles, with inheritance |
| 📋 Audit Log | Every action (upload, download, delete, move) is logged with IP and user-agent |
| 🖼 Thumbnails | Auto-generated via Intervention Image 3 (GD or Imagick) |
| ✂️ Full CRUD | Upload, rename, move, copy, soft-delete, restore, permanent delete |
| 🔗 Share Links | Temporary signed URLs with expiry, download limit, and optional password |
| 🧩 Editor Integration | CKEditor 5, TinyMCE, Summernote — single-file JS plugins included |
| 🎣 Event Hooks | FileUploaded, FileDeleted, FileDownloaded, FileMoved, FolderCreated |
| 📦 Artisan Commands | filemanager:install, filemanager:purge-trash |
| ⚡ Rate Limiting | Configurable upload rate limit per user/IP |
Requirements
- PHP 8.1+
- Laravel 10.x or 11.x
- Database MySQL 8.0+ / PostgreSQL 13+ / SQLite (testing)
- PHP Extensions
gdorimagick(for thumbnails),finfo(MIME detection)
Installation
1. Install via Composer
composer require ranitachi/filemanager-laravel
2. Run the installer
php artisan filemanager:install
This publishes config, migrations, and assets — then runs migrations automatically.
3. Manual install (alternative)
php artisan vendor:publish --tag=filemanager-config php artisan vendor:publish --tag=filemanager-migrations php artisan vendor:publish --tag=filemanager-assets php artisan migrate php artisan storage:link
Configuration
After publishing, edit config/filemanager.php:
// Storage driver 'disk' => env('FILEMANAGER_DISK', 'local'), 'storage_adapter' => \Ranitachi\FileManager\Storage\LocalAdapter::class, // Upload constraints 'max_upload_size_kb' => 10240, // 10 MB 'allowed_extensions' => ['jpg', 'png', 'pdf', 'docx', ...], // Rate limiting 'rate_limit' => [ 'uploads_per_minute' => 20, ], // Thumbnails 'thumbnails' => [ 'enabled' => true, 'width' => 300, 'height' => 300, 'driver' => 'gd', // or 'imagick' ],
.env Variables
FILEMANAGER_DISK=local FILEMANAGER_MAX_SIZE=10240 FILEMANAGER_SIGNED_URL_EXPIRE=60 # S3 / MinIO FILEMANAGER_ADAPTER=Ranitachi\FileManager\Storage\S3Adapter FILEMANAGER_S3_KEY=your-key FILEMANAGER_S3_SECRET=your-secret FILEMANAGER_S3_REGION=ap-southeast-1 FILEMANAGER_S3_BUCKET=your-bucket # MinIO only FILEMANAGER_S3_ENDPOINT=http://minio:9000 # ClamAV (optional) FILEMANAGER_AV_ENABLED=false FILEMANAGER_AV_HOST=localhost FILEMANAGER_AV_PORT=3310
API Reference
All API routes are prefixed with /api/v1/filemanager and require a Bearer token.
Files
| Method | Endpoint | Description |
|---|---|---|
GET |
/files |
Browse files (with folder_id, search, sort params) |
POST |
/upload |
Upload a file (multipart) |
GET |
/files/{id} |
Get file metadata |
GET |
/files/{id}/download |
Download file |
GET |
/files/{id}/preview |
Preview file (inline stream) |
PATCH |
/files/{id}/rename |
Rename file |
POST |
/files/{id}/move |
Move to another folder |
POST |
/files/{id}/copy |
Copy to another folder |
POST |
/files/{id}/restore |
Restore from trash |
DELETE |
/files/{id} |
Soft delete (pass permanent: true for hard delete) |
Folders
| Method | Endpoint | Description |
|---|---|---|
GET |
/folders |
Folder tree |
POST |
/folders |
Create folder |
PATCH |
/folders/{id} |
Rename folder |
DELETE |
/folders/{id} |
Delete folder |
Permissions
| Method | Endpoint | Description |
|---|---|---|
GET |
/{type}/{id}/permissions |
List permissions on file/folder |
POST |
/{type}/{id}/permissions |
Grant permission to user/role |
DELETE |
/permissions/{id} |
Revoke a specific permission |
Share Links
| Method | Endpoint | Description |
|---|---|---|
POST |
/files/{id}/share |
Create a share link |
DELETE |
/share/{token}/revoke |
Revoke a share link |
GET |
/share/{token} |
Public share page |
GET |
/share/{token}/download |
Public download |
Usage Examples
Via Facade
use Ranitachi\FileManager\Facades\FileManager; // Upload $file = FileManager::upload($request->file('document'), $folderId); // Browse with pagination $files = FileManager::browse($folderId, [ 'per_page' => 20, 'sort' => 'created_at', 'order' => 'desc', 'search' => 'report', ]); // Download (returns StreamedResponse) return FileManager::download($fileId); // Move FileManager::move($fileId, $newFolderId); // Delete (soft) FileManager::delete($fileId); // Delete (permanent — admin only) FileManager::delete($fileId, permanent: true);
Granting Permissions
use Ranitachi\FileManager\Services\PermissionService; $service = app(PermissionService::class); // Grant read+write to a specific user on a folder $service->grant( resource: $folder, grantableId: $userId, grantableType: App\Models\User::class, permissions: ['read', 'write'], expiresAt: now()->addDays(30), ); // Check permission $canDelete = $service->can($user, $file, 'delete'); // bool
Creating a Share Link
use Ranitachi\FileManager\Services\ShareService; $share = app(ShareService::class)->create( fileId: $file->id, expiresInMinutes: 1440, // 24 hours maxDownloads: 10, password: 'secret', ); echo $share->getShareUrl(); // https://yourapp.com/filemanager/share/AbCdEf...
Listening to Events
// In EventServiceProvider or config/filemanager.php 'listen' key: use Ranitachi\FileManager\Events\FileUploaded; class NotifyOnUpload { public function handle(FileUploaded $event): void { \Log::info("File uploaded: {$event->file->name} by user {$event->uploadedBy->id}"); } }
WYSIWYG Editor Integration
CKEditor 5
<script src="/vendor/filemanager/js/ckeditor-plugin.js" type="module"></script> <script type="module"> import { FileManagerPlugin } from '/vendor/filemanager/js/ckeditor-plugin.js'; ClassicEditor.create(document.querySelector('#editor'), { extraPlugins: [ FileManagerPlugin ], fileManager: { pickerUrl: '/filemanager/picker' }, }); </script>
TinyMCE
<script src="/vendor/filemanager/js/editors.js"></script> <script> FileManagerTinyMCE.init('#editor', { pickerUrl: '/filemanager/picker', }); </script>
Summernote
<script src="/vendor/filemanager/js/editors.js"></script> <script> $('#editor').summernote( FileManagerSummernote.config({ pickerUrl: '/filemanager/picker' }) ); </script>
The picker opens as a popup window. When a file is selected, a callback fires in the parent window and automatically inserts the file URL into the editor.
Artisan Commands
# One-step installation wizard php artisan filemanager:install # Purge files trashed more than 30 days ago (configurable) php artisan filemanager:purge-trash # Dry run (show what would be deleted) php artisan filemanager:purge-trash --dry-run # Custom retention period php artisan filemanager:purge-trash --days=7 # Schedule it in Console/Kernel.php or routes/console.php $schedule->command('filemanager:purge-trash --force')->weekly();
Security
This package implements multiple layers of defence:
Upload Security
- MIME type validated with PHP
finfo(content-based, not extension-based) - Extension cross-checked against MIME whitelist to prevent spoofing
- Magic bytes checked (blocks
<?php,#!/, etc.) - Storage path is UUID-based — original filename is never used in the path
- Filename is sanitised (strips
../, null bytes, dangerous characters) - Optional ClamAV antivirus scan hook
Access Control
- All endpoints require authentication
- Permission resolution: Super Admin → Owner → Explicit User → Role → Inherited → Deny
- Permissions support expiry dates
- Rate limiting on upload endpoints (configurable)
Share Links
- 64-character random token
- Configurable expiry + download limit
- Optional password protection (bcrypt)
- Signed URL for local storage previews
Testing
composer test # With coverage composer test-coverage
The test suite uses PestPHP and an in-memory SQLite database. Storage is faked via Storage::fake().
Publishing to Packagist
- Update
composer.jsonsesuai vendor/package final Anda di Packagist - Push to a public GitHub repository
- Go to packagist.org/packages/submit and enter your repository URL
- Set up the GitHub webhook for auto-updates on new releases
Roadmap
- Chunked upload for large files (> 100 MB)
- File versioning with restore to previous version
- CDN integration (CloudFront, Cloudflare, BunnyCDN)
- Image optimization (auto-resize, WebP conversion)
- AI auto-tagging via OpenAI Vision / Gemini
- OCR text extraction (Tesseract / AWS Textract)
- Multi-tenant storage isolation
- Goravel (Go) port for high-throughput microservice
License
MIT © Fachran