Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 7 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ A curated list of plugins for the [Pelican Panel](https://pelican.dev). Feel fre
For themes you have to install Node.js 22+ and Yarn beforehand. Example:

```bash
curl -sL https://deb.nodesource.com/setup_22.x | sudo -E bash -
curl -sL https://deb.nodesource.com/setup_22.x | sudo -E bash -
sudo apt install -y nodejs

npm i -g yarn
Expand All @@ -24,16 +24,17 @@ npm i -g yarn
## Plugins

- [Announcements](/announcements) - Create panel wide announcements to inform your users
- *[Arma Reforger Workshop](/arma-reforger-workshop) - Manage Arma Reforger workshop mods in server config
- \*[Arma Reforger Workshop](/arma-reforger-workshop) - Manage Arma Reforger workshop mods in server config
- [Billing](/billing) - Allows users to purchase servers via Stripe - **Proof of Concept - Do absolutely NOT use in production!**
- \*[Backup Templates](/backup-templates) - Create reusable backup ignore presets for specific servers.
- [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
- \*[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
- \*[PasteFox Share](/pastefox-share) - Share console logs via pastefox.com
- [Player Counter](/player-counter) - Show connected players count for game servers
- *[PocketID Provider](/pocketid-provider) - Allows you to use PocketID as an OAuth provider
- \*[PocketID Provider](/pocketid-provider) - Allows you to use PocketID as an OAuth provider
- [Register](/register) - Enable user self-registration on all panels
- [Robo Avatars](/robo-avatars) - Adds RoboHash as avatar provider
- [Rust uMod](/rust-umod) - Download Rust plugins from uMod
Expand All @@ -44,7 +45,7 @@ npm i -g yarn
- [Tickets](/tickets) - Simple ticket system for user support
- [User Creatable Servers](/user-creatable-servers) - Allow users to create their own servers

_* Third party plugins_
_\* Third party plugins_

## Themes

Expand Down
674 changes: 674 additions & 0 deletions backup-templates/LICENSE

Large diffs are not rendered by default.

12 changes: 12 additions & 0 deletions backup-templates/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Backup Templates (by Ebnater)

Create reusable backup ignore presets for specific servers.

This plugin adds per-server backup templates that can be selected when creating a backup. Selecting a template automatically fills the ignored paths field with the saved preset.

## Features

- Create backup templates with predefined ignored paths
- Store multiple templates per server
- Apply templates directly from the backup creation form
- Keep templates separated per server
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
public function up(): void
{
Schema::create('backup_templates', function (Blueprint $table) {
$table->increments('id');

$table->unsignedInteger('server_id');
$table->foreign('server_id')->references('id')->on('servers')->cascadeOnDelete();

$table->string('name');
$table->text('ignored')->nullable();

$table->timestamps();

$table->unique(['server_id', 'name']);
});
}

public function down(): void
{
Schema::dropIfExists('backup_templates');
}
};
8 changes: 8 additions & 0 deletions backup-templates/lang/de/server/user.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?php

return [
'permissions' => [
'backupTemplates_desc' => 'Berechtigungen zum Verwalten von Backup-Vorlagen für diesen Server.',
'backupTemplates_create' => 'Erlaubt es einem Benutzer, Backup-Vorlagen für diesen Server zu erstellen, zu bearbeiten und zu löschen.',
],
];
22 changes: 22 additions & 0 deletions backup-templates/lang/de/strings.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php

return [
'template' => 'Backup Template|Backup Templates',

'permissions' => [
'group' => 'Berechtigungen zum Verwalten von Backup-Vorlagen für diesen Server.',
'create' => 'Erlaubt es einem Benutzer, Backup-Vorlagen für diesen Server zu erstellen, zu bearbeiten und zu löschen.',
],

'fields' => [
'name' => 'Name',
'ignored' => 'Ignorierte Dateien und Ordner',
'ignored_help' => 'Verwenden Sie einen Pfad pro Zeile, der dem Pelican-Backup-Ignore-Format entspricht.',
],

'backup_form' => [
'template' => 'Ignorier-Vorlage',
'template_placeholder' => 'Keine Vorlage ausgewählt',
'template_help' => 'Verwenden Sie eine Vorlage, um ignorierte Pfade automatisch auszufüllen.',
],
];
8 changes: 8 additions & 0 deletions backup-templates/lang/en/server/user.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?php

return [
'permissions' => [
'backupTemplates_desc' => 'Permissions for managing backup templates for this server.',
'backupTemplates_create' => 'Allows a user to create, edit, and delete backup templates for this server.',
],
];
22 changes: 22 additions & 0 deletions backup-templates/lang/en/strings.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php

return [
'template' => 'Backup Template|Backup Templates',

'permissions' => [
'group' => 'Permissions for managing backup templates for this server.',
'create' => 'Allows a user to create, edit, and delete backup templates for this server.',
],

'fields' => [
'name' => 'Name',
'ignored' => 'Ignored Files and Folders',
'ignored_help' => 'Use one path per line, matching Pelican backup ignore format.',
],

'backup_form' => [
'template' => 'Ignore Preset',
'template_placeholder' => 'No preset selected',
'template_help' => 'Select a template to auto-fill ignored paths.',
],
];
17 changes: 17 additions & 0 deletions backup-templates/plugin.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"id": "backup-templates",
"name": "Backup Templates",
"author": "Ebnater",
"version": "1.0.0",
"description": "Provides backup templates for your Pelican site.",
"category": "plugin",
"url": "https://github.com/pelican-dev/plugins/tree/main/backup-templates",
"update_url": null,
"namespace": "Ebnater\\BackupTemplates",
"class": "BackupTemplatesPlugin",
"panels": [
"server"
],
"panel_version": null,
"composer_packages": []
}
23 changes: 23 additions & 0 deletions backup-templates/src/BackupTemplatesPlugin.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php

namespace Ebnater\BackupTemplates;

use Filament\Contracts\Plugin;
use Filament\Panel;

class BackupTemplatesPlugin implements Plugin
{
public function getId(): string
{
return 'backup-templates';
}

public function register(Panel $panel): void
{
$id = str($panel->getId())->title();

$panel->discoverResources(plugin_path($this->getId(), "src/Filament/$id/Resources"), "Ebnater\\BackupTemplates\\Filament\\$id\\Resources");
}

public function boot(Panel $panel): void {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
<?php

namespace Ebnater\BackupTemplates\Filament\Server\Resources\BackupTemplates;

use App\Enums\SubuserPermission;
use App\Models\Server;
use Ebnater\BackupTemplates\Filament\Server\Resources\BackupTemplates\Pages\ListBackupTemplates;
use Ebnater\BackupTemplates\Models\BackupTemplate;
use Filament\Actions\DeleteAction;
use Filament\Actions\EditAction;
Comment on lines +9 to +10
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🌐 Web query:

Filament v3 table recordActions EditAction namespace

💡 Result:

In Filament v3, the table “Edit” row action class is:

use Filament\Tables\Actions\EditAction;

Use it in your table like:

->actions([
    EditAction::make(),
])

(For non-table actions, the namespace is Filament\Actions\EditAction.) [1][2]


🏁 Script executed:

cat -n backup-templates/src/Filament/Server/Resources/BackupTemplates/BackupTemplateResource.php

Repository: pelican-dev/plugins

Length of output: 4017


Use correct namespace for table record actions.

Lines 9-10 import from Filament\Actions, but since these actions are used in ->recordActions() (line 82-85), they should import from Filament\Tables\Actions instead:

use Filament\Tables\Actions\DeleteAction;
use Filament\Tables\Actions\EditAction;

The Filament\Actions namespace is for page-level header actions, not table record actions.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@backup-templates/src/Filament/Server/Resources/BackupTemplates/BackupTemplateResource.php`
around lines 9 - 10, The imports for DeleteAction and EditAction are using the
wrong namespace; update the top of BackupTemplateResource.php to import
DeleteAction and EditAction from Filament\Tables\Actions (not Filament\Actions)
so the actions used in the recordActions() call (see recordActions method around
where DeleteAction and EditAction are referenced) resolve to the table record
action classes.

use Filament\Facades\Filament;
use Filament\Forms\Components\Textarea;
use Filament\Forms\Components\TextInput;
use Filament\Resources\Resource;
use Filament\Schemas\Schema;
use Filament\Tables\Columns\TextColumn;
use Filament\Tables\Table;
use Illuminate\Database\Eloquent\Builder;

class BackupTemplateResource extends Resource
{
protected static ?string $model = BackupTemplate::class;

protected static ?int $navigationSort = 31;

protected static string|\BackedEnum|null $navigationIcon = 'tabler-template';

public static function canAccess(): bool
{
/** @var Server|null $server */
$server = Filament::getTenant();

return $server !== null
&& user()?->can(SubuserPermission::BackupRead, $server)
&& parent::canAccess();
}

public static function getNavigationLabel(): string
{
return trans_choice('backup-templates::strings.template', 2);
}

public static function getModelLabel(): string
{
return trans_choice('backup-templates::strings.template', 1);
}

public static function getPluralModelLabel(): string
{
return trans_choice('backup-templates::strings.template', 2);
}

public static function form(Schema $schema): Schema
{
return $schema
->components([
TextInput::make('name')
->label(trans('backup-templates::strings.fields.name'))
->required()
->maxLength(255),
Textarea::make('ignored')
->label(trans('backup-templates::strings.fields.ignored'))
->helperText(trans('backup-templates::strings.fields.ignored_help'))
->rows(8)
->columnSpanFull(),
]);
}

public static function table(Table $table): Table
{
return $table
->columns([
TextColumn::make('name')
->label(trans('backup-templates::strings.fields.name'))
->searchable()
->sortable(),
TextColumn::make('ignored')
->label(trans('backup-templates::strings.fields.ignored'))
->lineClamp(2)
->placeholder('-'),
])
->recordActions([
EditAction::make(),
DeleteAction::make(),
]);
}

public static function getPages(): array
{
return [
'index' => ListBackupTemplates::route('/'),
];
}

public static function getEloquentQuery(): Builder
{
/** @var Server|null $server */
$server = Filament::getTenant();

return parent::getEloquentQuery()
->when($server, fn (Builder $query) => $query->where('server_id', $server->id));
}
Comment on lines +95 to +102
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Potential data exposure if query executed without tenant.

The when($server, ...) conditional means all records are returned if $server is null. While canAccess() gates resource entry, if getEloquentQuery() is invoked through other code paths, it could expose templates from all servers.

🛡️ Proposed defensive fix
 public static function getEloquentQuery(): Builder
 {
     /** `@var` Server|null $server */
     $server = Filament::getTenant();

+    if (!$server) {
+        return parent::getEloquentQuery()->whereRaw('1 = 0');
+    }
+
     return parent::getEloquentQuery()
-        ->when($server, fn (Builder $query) => $query->where('server_id', $server->id));
+        ->where('server_id', $server->id);
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@backup-templates/src/Filament/Server/Resources/BackupTemplates/BackupTemplateResource.php`
around lines 95 - 102, getEloquentQuery currently uses when($server, ...) so if
Filament::getTenant() returns null the query returns all records; change
getEloquentQuery (the method) so it always constrains results to a tenant — use
Filament::getTenant() into $server and, if $server is present apply
where('server_id', $server->id), otherwise force an empty result set (e.g. add a
fallback that restricts to no rows) to prevent accidental cross-tenant exposure
instead of leaving the query unfiltered.

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<?php

namespace Ebnater\BackupTemplates\Filament\Server\Resources\BackupTemplates\Pages;

use App\Models\Server;
use Ebnater\BackupTemplates\Filament\Server\Resources\BackupTemplates\BackupTemplateResource;
use Filament\Actions\CreateAction;
use Filament\Facades\Filament;
use Filament\Resources\Pages\ListRecords;

class ListBackupTemplates extends ListRecords
{
protected static string $resource = BackupTemplateResource::class;

public function getBreadcrumbs(): array
{
return [];
}

protected function getHeaderActions(): array
{
return [
CreateAction::make()
->authorize(function (): bool {
/** @var Server|null $server */
$server = Filament::getTenant();

return $server !== null && user()?->can('backupTemplates.create', $server);
})
->createAnother(false)
->mutateDataUsing(function (array $data): array {
/** @var Server|null $server */
$server = Filament::getTenant();

if ($server) {
$data['server_id'] = $server->id;
}

return $data;
}),
];
}
}
28 changes: 28 additions & 0 deletions backup-templates/src/Models/BackupTemplate.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php

namespace Ebnater\BackupTemplates\Models;

use App\Models\Server;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;

/**
* @property int $id
* @property int $server_id
* @property string $name
* @property string|null $ignored
* @property Server $server
*/
class BackupTemplate extends Model
{
protected $fillable = [
'server_id',
'name',
'ignored',
];

public function server(): BelongsTo
{
return $this->belongsTo(Server::class, 'server_id');
}
}
Loading
Loading