Skip to content

Commit

Permalink
Delete old reference files (#15)
Browse files Browse the repository at this point in the history
  • Loading branch information
aerni authored Apr 11, 2023
1 parent cf8fa16 commit 60d95cd
Show file tree
Hide file tree
Showing 7 changed files with 174 additions and 3 deletions.
42 changes: 42 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,22 @@ return [

'expiry' => null,

/*
|--------------------------------------------------------------------------
| Cleanup Scope
|--------------------------------------------------------------------------
|
| The scope to use when cleaning up your zip references with the scheduled command.
|
| Options:
| "expired": Only delete expired reference files
| "all": Delete all reference files excluding unexpired files
| "force": Delete all reference files including unexpired files
|
*/

'cleanup' => 'expired',

];
```

Expand Down Expand Up @@ -92,6 +108,32 @@ If you want to expire your links after a certain time, you can either set the ex
{{ zip:images expiry="60" }}
```

## Cleanup Old References

Zipper saves an encrypted instance of the Zip class every time it returns a URL. These reference files are stored in `storage/zipper/{id}`. Whenever a user downloads a zip, Zipper will retrieve and decrypt the requested Zip instance.

With time, the amound of saved reference files will grow. To get this under control, Zipper provides a scheduled command that will daily delete old reference files. Just make sure that your Scheduler is running.

### Cleanup Scopes

There are a couple of cleanup scopes you can define in the config:

| Option | Description |
|-----------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `expired` | Only delete expired references files. This only affects references of zips that used the `expiry` option |
| `all` | Delete all reference files excluding unexpired files. This will delete references of zips that didn't use the expiry option as well as expired zips. It will not delete unexpired zips. |
| `force` | Delete all reference files including unexpired files. This will completely wipe all references. |

### Clean Command

You may also use the `clean` command to delete reference files at your will. The scope defaults to `expired`.

```bash
php please zipper:clean
php please zipper:clean --scope=all
php please zipper:clean --scope=force
```

## Advanced Usage

You may also use this addon programmatically as shown below.
Expand Down
16 changes: 16 additions & 0 deletions config/zipper.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,20 @@

'expiry' => null,

/*
|--------------------------------------------------------------------------
| Cleanup Scope
|--------------------------------------------------------------------------
|
| The scope to use when cleaning up your zip references with the scheduled command.
|
| Options:
| "expired": Only delete expired reference files
| "all": Delete all reference files excluding unexpired files
| "force": Delete all reference files including unexpired files
|
*/

'cleanup' => 'expired',

];
23 changes: 23 additions & 0 deletions src/Commands/CleanReferenceFilesCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php

namespace Aerni\Zipper\Commands;

use Aerni\Zipper\Jobs\CleanReferenceFilesJob;
use Illuminate\Console\Command;
use Statamic\Console\RunsInPlease;

class CleanReferenceFilesCommand extends Command
{
use RunsInPlease;

protected $signature = 'zipper:clean {--scope=expired}';

protected $description = 'Delete old zipper reference files';

public function handle()
{
CleanReferenceFilesJob::dispatch($this->option('scope'));

$this->info('Successfully dispatched the cleanup job.');
}
}
36 changes: 36 additions & 0 deletions src/Jobs/CleanReferenceFilesJob.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?php

namespace Aerni\Zipper\Jobs;

use Aerni\Zipper\Facades\ZipperStore;
use Illuminate\Bus\Queueable;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;

class CleanReferenceFilesJob
{
use Dispatchable;
use InteractsWithQueue;
use Queueable;
use SerializesModels;

public function __construct(protected string $scope = 'expired')
{
//
}

public function handle(): void
{
$zips = ZipperStore::all();

$zips = match (true) {
($this->scope === 'expired') => $zips->filter(fn ($zip) => $zip->expired()), // Only delete expired reference files
($this->scope === 'all') => $zips->filter(fn ($zip) => $zip->expired() || empty($zip->expiry())), // Delete all reference files excluding unexpired files
($this->scope === 'force') => $zips, // Delete all reference files including unexpired files
default => throw new \Exception('Please provide a valid cleanup scope.')
};

$zips->each(fn ($zip) => $zip->deleteReferenceFile());
}
}
12 changes: 12 additions & 0 deletions src/ServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,27 @@

namespace Aerni\Zipper;

use Aerni\Zipper\Commands\CleanReferenceFilesCommand;
use Statamic\Providers\AddonServiceProvider;

class ServiceProvider extends AddonServiceProvider
{
protected $commands = [
CleanReferenceFilesCommand::class,
];

protected $routes = [
'actions' => __DIR__.'/../routes/actions.php',
];

protected $tags = [
ZipperTags::class,
];

protected function schedule($schedule)
{
$scope = config('zipper.cleanup', 'expired');

$schedule->command(CleanReferenceFilesCommand::class, ["--scope={$scope}"])->daily();
}
}
28 changes: 25 additions & 3 deletions src/Zip.php
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,20 @@ public function expiry(int $expiry = null): int|self
return $this;
}

/**
* Check if the stored zip reference file is expired.
*/
public function expired(): bool
{
if (empty($this->expiry)) {
return false;
}

return ZipperStore::createdAt($this->id())
->addMinutes($this->expiry)
->isPast();
}

/**
* Returns the route that handles creating the zip.
*/
Expand All @@ -94,9 +108,17 @@ public function url(): string
*/
protected function storeReferenceFile(): self
{
if (! ZipperStore::exists($this->id())) {
ZipperStore::put($this->id(), $this);
}
ZipperStore::put($this->id(), $this);

return $this;
}

/**
* Delete the zip reference file.
*/
public function deleteReferenceFile(): self
{
ZipperStore::delete($this->id());

return $this;
}
Expand Down
20 changes: 20 additions & 0 deletions src/ZipperStore.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
namespace Aerni\Zipper;

use Illuminate\Contracts\Filesystem\Filesystem;
use Illuminate\Support\Carbon;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Crypt;
use Illuminate\Support\Facades\Storage;

Expand All @@ -18,6 +20,12 @@ public function __construct()
]);
}

public function all(): Collection
{
return collect($this->store->allFiles())
->map(fn ($file) => $this->get($file));
}

public function put(string $path, Zip $zip): bool
{
return $this->store->put($path, Crypt::encrypt($zip));
Expand All @@ -36,4 +44,16 @@ public function exists(string $path): bool
{
return $this->store->exists($path);
}

public function delete(string $path): bool
{
return $this->store->delete($path);
}

public function createdAt(string $path): Carbon
{
$createdAt = filemtime(storage_path('statamic/zipper').'/'.$path);

return Carbon::createFromTimestamp($createdAt);
}
}

0 comments on commit 60d95cd

Please sign in to comment.