diff --git a/README.md b/README.md index dfe5c2c..443685f 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,7 @@ npm i -g yarn - [Billing](/billing) - Allows users to purchase servers via Stripe - **Proof of Concept - Do absolutely NOT use in production!** - [Generic OIDC Providers](/generic-oidc-providers) - Create generic OIDC providers for authentication - [Legal Pages](/legal-pages) - Adds legal pages (Imprint, Privacy Policy, ToS) to the panel +- [McLogCleaner](/mclogcleaner) - Delete old logs with ease - [MCLogs Uploader](/mclogs-uploader) - Upload console logs to mclo.gs - [Minecraft Modrinth](/minecraft-modrinth) - Download Minecraft mods & plugins from Modrinth - [PasteFox Share](/pastefox-share) - Share console logs via pastefox.com diff --git a/mclogcleaner/README.md b/mclogcleaner/README.md new file mode 100644 index 0000000..e432dd5 --- /dev/null +++ b/mclogcleaner/README.md @@ -0,0 +1,15 @@ +# McLogCleaner (by JuggleGaming) + +McLogCleaner automatically deletes all `.log.gz` files from the server’s `logs` folder. + +> **Note:** `latest.log` will always remain intact and is never deleted. + +## Setup +To use this plugin, add `mclogcleaner` as a _feature_ to the egg you want to run it with. + +## Log Deletion Options +When you click **Delete logs**, a dropdown menu appears where you can choose the **minimum age (in days)** of log files to delete: +- Logs older than 7 days +- Logs older than 30 days +- All logs (regardless of age) +- A custom age in days diff --git a/mclogcleaner/plugin.json b/mclogcleaner/plugin.json new file mode 100644 index 0000000..6341a26 --- /dev/null +++ b/mclogcleaner/plugin.json @@ -0,0 +1,15 @@ +{ + "id": "mclogcleaner", + "name": "McLogCleaner", + "author": "JuggleGaming", + "version": "1.1.1", + "description": "Clean your Minecraft-logs with ease", + "category": "plugin", + "url": "https://github.com/pelican-dev/plugins/tree/main/mclogcleaner", + "update_url": null, + "namespace": "JuggleGaming\\McLogCleaner", + "class": "McLogCleanerPlugin", + "panels": null, + "panel_version": null, + "composer_packages": null +} diff --git a/mclogcleaner/src/Filament/Components/Actions/McLogCleanAction.php b/mclogcleaner/src/Filament/Components/Actions/McLogCleanAction.php new file mode 100644 index 0000000..a05c66f --- /dev/null +++ b/mclogcleaner/src/Filament/Components/Actions/McLogCleanAction.php @@ -0,0 +1,151 @@ +hidden(function () { + /** @var Server|null $server */ + $server = Filament::getTenant(); + if (!$server) { + return true; + } + $server->loadMissing('egg'); + $features = $server->egg->features ?? []; + + return !in_array('mclogcleaner', $features, true); + }); + + $this->label('Delete logs'); + $this->icon('tabler-trash'); + $this->color('danger'); + $this->size(Size::ExtraLarge); + $this->requiresConfirmation() + ->modalHeading('Delete logs') + ->modalDescription('Choose which logs should be deleted.') + ->modalSubmitActionLabel('Delete logs') + ->form([ + Select::make('mode') + ->label('Delete logs') + ->options([ + 7 => 'Older than 7 days', + 30 => 'Older than 30 days', + -1 => 'Delete all logs', + 'custom' => 'Custom (days)', + ]) + ->default(7) + ->required() + ->reactive(), + TextInput::make('custom_days') + ->label('Delete logs older than (days)') + ->numeric() + ->minValue(1) + ->maxValue(365) + ->placeholder('e.g. 14') + ->required(fn ($get) => $get('mode') === 'custom') + ->visible(fn ($get) => $get('mode') === 'custom'), + ]); + + $this->action(function (array $data) { + /** @var Server|null $server */ + $server = Filament::getTenant(); + $mode = $data['mode']; + if ($mode !== 'custom') { + $mode = (int) $mode; + } + if ($mode === 'custom') { + $days = max(1, (int) $data['custom_days']); + } elseif ($mode === -1) { + $days = 0; + } else { + $days = $mode; + } + try { + $files = Http::daemon($server->node) + ->get("/api/servers/{$server->uuid}/files/list-directory", [ + 'directory' => 'logs', + ]) + ->throw() + ->json(); + if (!is_array($files)) { + throw new Exception('Invalid log directory response.'); + } + $threshold = now()->subDays($days)->startOfDay(); + $logsToDelete = collect($files) + ->filter(fn ($file) => str_ends_with($file['name'], '.log.gz')) + ->filter(function ($file) use ($days, $threshold) { + if ($days === 0) { + return true; + } + $logDate = $this->extractLogDate($file['name']); + if (!$logDate) { + return false; + } + + return $logDate->lessThan($threshold); + }) + ->pluck('name') + ->map(fn ($name) => 'logs/' . $name) + ->values() + ->all(); + if (empty($logsToDelete)) { + Notification::make() + ->title('McLogCleaner') + ->body('No logs matching your selection were found.') + ->success() + ->send(); + + return; + } + Http::daemon($server->node) + ->post("/api/servers/{$server->uuid}/files/delete", [ + 'root' => '/', + 'files' => $logsToDelete, + ]) + ->throw(); + Notification::make() + ->title('Logfolder cleaned') + ->body(count($logsToDelete) . ' files were deleted.') + ->success() + ->send(); + } catch (\Throwable $e) { + report($e); + Notification::make() + ->title('Cleanup failed.') + ->body('An error occurred while deleting log files. Please try again later.') + ->danger() + ->send(); + } + }); + } + + private function extractLogDate(string $filename): ?Carbon + { + if (preg_match('/(\d{4}-\d{2}-\d{2})/', $filename, $matches)) { + $date = Carbon::createFromFormat('Y-m-d', $matches[1]); + + return $date ? $date->startOfDay() : null; + } + + return null; + } +} diff --git a/mclogcleaner/src/McLogCleanerPlugin.php b/mclogcleaner/src/McLogCleanerPlugin.php new file mode 100644 index 0000000..d33b378 --- /dev/null +++ b/mclogcleaner/src/McLogCleanerPlugin.php @@ -0,0 +1,24 @@ +