A modular Laravel 10+ application that monitors a filesystem directory for file changes and performs dynamic actions based on file type.
./dev.sh
- ✅ Monitor file creation and deletion
- 🖼 Automatically optimize
.jpg/.jpegfiles for web - 🥓 Append Bacon Ipsum to
.txtfiles - 🌐 Send
.jsonfiles to an HTTP endpoint - 📦 Extract
.zipfiles - 😂 Replace deleted files with meme images from the Meme API
- 🧪 Fully unit-tested with service-level isolation
- 🔁 Cleanly extendable for future file types
This application was designed around the following principles:
Each file type is handled by its own dedicated service class. Every service implements a common interface:
interface FileWatchServiceInterface {
public function fileTypes(): array;
public function handle(string $path): void;
}
This makes it trivial to add new file watchers without altering existing logic.app/
├── Console/
│ └── Commands/
│ └── WatchFileSystem.php
├── Services/
│ └── FileWatchers/
│ ├── DispatcherService.php
│ ├── ImageOptimizerService.php
│ ├── JsonWebhookService.php
│ ├── TxtAppenderService.php
│ ├── ZipExtractorService.php
│ └── MemeRestorerService.php
./tests.bash
tests/
└── Unit/
└── Services/
└── FileWatchers/
├── ImageOptimizerServiceTest.php
├── JsonWebhookServiceTest.php
├── TxtAppenderServiceTest.php
├── ZipExtractorServiceTest.php
└── MemeRestorerServiceTest.php
A single DispatcherService is responsible for routing file events to the correct service based on file extension. This separation of concerns keeps the watcher logic thin and maintainable.
A custom Monolog channel (watcher) provides structured, timestamped log entries that are easy to tail or parse
Spatie's file-system-watcher only supports onFileCreated() and onFileDeleted() — no native onFileModified().
Solution: Simulated file modification detection was scoped out for now. It can be added later using:
php-inotify (Linux-only)
Scheduled checksum-based polling
Replacing the watcher backend
APIs like Meme API and Bacon Ipsum may be slow, fail, or return invalid data.
Solution: All API calls:
Use timeout + structured error logging
Include response duration in logs
Validate expected JSON fields
Some JPEGs were too small to optimize (e.g. 10×10 test images).
Solution: Tests use realistic image sizes and assert size reductions only when measurable.
To add a new file watcher (e.g. for .csv, .mp4, .pdf):
Create a New Service
class CsvArchiverService implements FileWatchServiceInterface {
public function fileTypes(): array { return ['csv']; }
public function handle(string $path): void {
// your logic here
}
}Register It in DispatcherService
$this->services = [
$image, $json, $txt, $zip, $csv
];Modify detection support (via polling or inotify)
Watcher service auto-discovery via tagged services
.meta.json metadata tracking per file
File quarantine mode
Web UI dashboard for logs/status
php artisan fs:watch
Logs output to:
storage/logs/fs-watcher.log
php artisan test
Built with Laravel 10, Spatie File Watcher, Guzzle, and clean architectural separation.
MIT