pelican-panel-plugins

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Pelican Panel Plugins

Pelican面板插件

Pelican is an open-source game server management panel built on Laravel + FilamentPHP. Plugins let you add functionality without touching core files.
⚠️ The plugin system is still in active development — some features may change.
Pelican是一款基于Laravel + FilamentPHP构建的开源游戏服务器管理面板。通过插件,你无需修改核心文件即可添加功能。
⚠️ 插件系统仍在积极开发中——部分功能可能会发生变更。

Quick Reference

快速参考

📖 Detailed Guides:
  • FilamentPHP Patterns - Resources, pages, widgets, actions, relation managers
  • Advanced Patterns - Models, services, routes, commands, events
  • Complete Plugin Walkthrough - Step-by-step example
📖 详细指南:
  • FilamentPHP模式 - 资源、页面、小部件、操作、关联管理器
  • 进阶模式 - 模型、服务、路由、命令、事件
  • 完整插件开发教程 - 分步示例

Scaffolding a New Plugin

搭建新插件

Run inside the panel directory (
/var/www/pelican
by default):
bash
php artisan p:plugin:make
This creates the basic structure with
plugin.json
, main plugin class, service provider, and config file.
Critical: The plugin folder name must exactly match the
id
field in
plugin.json
.
在面板目录(默认路径为
/var/www/pelican
)内运行:
bash
php artisan p:plugin:make
此命令会创建包含
plugin.json
、主插件类、服务提供者和配置文件的基础结构。
重要提示: 插件文件夹名称必须与
plugin.json
中的
id
字段完全匹配。

Plugin Structure

插件结构

plugins/my-plugin/
├── plugin.json              # Metadata and configuration
├── config/
│   └── my-plugin.php       # Config values (use env vars)
├── database/
│   └── migrations/         # Auto-discovered migrations
├── lang/                   # Translations (namespaced: my-plugin::strings.key)
├── resources/
│   └── views/              # Blade views (namespaced: my-plugin::view-name)
├── routes/                 # Optional route files
└── src/                    # App logic (PSR-4 autoloaded)
    ├── MyPlugin.php        # Main plugin class
    ├── Filament/
    │   ├── Admin/          # Admin panel components
    │   │   ├── Pages/
    │   │   ├── Resources/
    │   │   └── Widgets/
    │   ├── App/            # Server list panel
    │   └── Server/         # Server management panel
    ├── Models/
    ├── Policies/           # Auto-discovered
    ├── Providers/          # Auto-discovered service providers
    ├── Console/Commands/   # Auto-discovered artisan commands
    └── Http/
        └── Controllers/
Everything in standard Laravel locations is auto-discovered: migrations, providers, commands, policies.
plugins/my-plugin/
├── plugin.json              # 元数据与配置
├── config/
│   └── my-plugin.php       # 配置值(使用环境变量)
├── database/
│   └── migrations/         # 自动发现的迁移文件
├── lang/                   # 翻译文件(命名空间:my-plugin::strings.key)
├── resources/
│   └── views/              # Blade视图(命名空间:my-plugin::view-name)
├── routes/                 # 可选路由文件
└── src/                    # 应用逻辑(PSR-4自动加载)
    ├── MyPlugin.php        # 主插件类
    ├── Filament/
    │   ├── Admin/          # 管理面板组件
    │   │   ├── Pages/
    │   │   ├── Resources/
    │   │   └── Widgets/
    │   ├── App/            # 服务器列表面板
    │   └── Server/         # 服务器管理面板
    ├── Models/
    ├── Policies/           # 自动发现
    ├── Providers/          # 自动发现的服务提供者
    ├── Console/Commands/   # 自动发现的Artisan命令
    └── Http/
        └── Controllers/
标准Laravel目录下的所有内容都会被自动发现:迁移文件、服务提供者、命令、策略。

plugin.json

plugin.json

json
{
    "id": "my-plugin",
    "name": "My Plugin",
    "author": "Your Name",
    "version": "1.0.0",
    "description": "Short description",
    "category": "plugin",
    "namespace": "MyName\\MyPlugin",
    "class": "MyPlugin",
    "panels": ["admin", "server"],
    "panel_version": "^1.2.0",
    "composer_packages": {
        "vendor/package": "^1.0"
    }
}
FieldRequiredNotes
id
Must match folder name
namespace
PHP namespace root (use
\\
for backslashes)
class
Main class name (in
src/
)
category
plugin
,
theme
, or
language
panels
NoArray of panel IDs or omit for all panels
panel_version
NoMinimum panel version (e.g.,
^1.2.0
)
composer_packages
NoExternal dependencies
json
{
    "id": "my-plugin",
    "name": "My Plugin",
    "author": "Your Name",
    "version": "1.0.0",
    "description": "Short description",
    "category": "plugin",
    "namespace": "MyName\\MyPlugin",
    "class": "MyPlugin",
    "panels": ["admin", "server"],
    "panel_version": "^1.2.0",
    "composer_packages": {
        "vendor/package": "^1.0"
    }
}
字段是否必填说明
id
必须与文件夹名称匹配
namespace
PHP命名空间根目录(使用
\\
作为分隔符)
class
主类名称(位于
src/
目录下)
category
可选值:
plugin
theme
language
panels
面板ID数组,省略则适配所有面板
panel_version
最低面板版本要求(例如:
^1.2.0
composer_packages
外部依赖包

Main Plugin Class

主插件类

Located in
src/{ClassName}.php
:
php
namespace MyName\MyPlugin;

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

class MyPlugin implements Plugin
{
    public function getId(): string
    {
        return 'my-plugin';
    }

    public function register(Panel $panel): void
    {
        $id = str($panel->getId())->title(); // "Admin", "App", "Server"

        // Auto-discover Filament components
        $panel->discoverPages(
            plugin_path($this->getId(), "src/Filament/$id/Pages"),
            "MyName\\MyPlugin\\Filament\\$id\\Pages"
        );
        $panel->discoverResources(
            plugin_path($this->getId(), "src/Filament/$id/Resources"),
            "MyName\\MyPlugin\\Filament\\$id\\Resources"
        );
        $panel->discoverWidgets(
            plugin_path($this->getId(), "src/Filament/$id/Widgets"),
            "MyName\\MyPlugin\\Filament\\$id\\Widgets"
        );
    }

    public function boot(Panel $panel): void
    {
        //
    }
}
位于
src/{ClassName}.php
php
namespace MyName\MyPlugin;

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

class MyPlugin implements Plugin
{
    public function getId(): string
    {
        return 'my-plugin';
    }

    public function register(Panel $panel): void
    {
        $id = str($panel->getId())->title(); // "Admin", "App", "Server"

        // 自动发现Filament组件
        $panel->discoverPages(
            plugin_path($this->getId(), "src/Filament/$id/Pages"),
            "MyName\\MyPlugin\\Filament\\$id\\Pages"
        );
        $panel->discoverResources(
            plugin_path($this->getId(), "src/Filament/$id/Resources"),
            "MyName\\MyPlugin\\Filament\\$id\\Resources"
        );
        $panel->discoverWidgets(
            plugin_path($this->getId(), "src/Filament/$id/Widgets"),
            "MyName\\MyPlugin\\Filament\\$id\\Widgets"
        );
    }

    public function boot(Panel $panel): void
    {
        //
    }
}

The Three Filament Panels

三个Filament面板

Panel IDAreaUse Case
admin
Admin areaFull CRUD for resources, settings, management
app
Server listMinimal UI (no nav by default)
server
Server managementTenant-scoped (current server context)
面板ID区域使用场景
admin
管理后台资源的完整CRUD操作、设置、管理功能
app
服务器列表极简UI(默认无导航)
server
服务器管理租户范围(当前服务器上下文)

Enabling Navigation on
app
Panel

app
面板启用导航

php
use App\Filament\App\Resources\Servers\ServerResource;
use App\Enums\CustomizationKey;

public function register(Panel $panel): void
{
    parent::register($panel);

    if ($panel->getId() === 'app') {
        ServerResource::embedServerList();
        $panel->navigation(true);
        $panel->topbar(function () {
            $nav = user()?->getCustomization(CustomizationKey::TopNavigation);
            return in_array($nav, ['topbar', 'mixed', true], true);
        });
        $panel->clearCachedComponents();
    }
}
php
use App\Filament\App\Resources\Servers\ServerResource;
use App\Enums\CustomizationKey;

public function register(Panel $panel): void
{
    parent::register($panel);

    if ($panel->getId() === 'app') {
        ServerResource::embedServerList();
        $panel->navigation(true);
        $panel->topbar(function () {
            $nav = user()?->getCustomization(CustomizationKey::TopNavigation);
            return in_array($nav, ['topbar', 'mixed', true], true);
        });
        $panel->clearCachedComponents();
    }
}

Extending Core Resources

扩展核心资源

Call static methods on core classes inside a service provider's
register()
:
php
use App\Filament\Admin\Resources\Users\UserResource;
use App\Filament\Admin\Resources\Servers\ServerResource;
use App\Models\Role;

public function register(): void
{
    // Add a relation manager tab
    ServerResource::registerCustomRelations(MyRelationManager::class);
    
    // Register permissions
    Role::registerCustomDefaultPermissions('myModel');
    Role::registerCustomModelIcon('myModel', 'tabler-star');
}
Available customization traits (check
app/Traits/Filament/
for all):
  • CanModifyResource
    - Relation managers, custom actions
  • CanCustomizePage
    - Widgets, header actions
  • CanModifyForm
    /
    CanModifyTable
    - Form/table hooks
服务提供者的
register()
方法
中调用核心类的静态方法:
php
use App\Filament\Admin\Resources\Users\UserResource;
use App\Filament\Admin\Resources\Servers\ServerResource;
use App\Models\Role;

public function register(): void
{
    // 添加关联管理器标签页
    ServerResource::registerCustomRelations(MyRelationManager::class);
    
    // 注册权限
    Role::registerCustomDefaultPermissions('myModel');
    Role::registerCustomModelIcon('myModel', 'tabler-star');
}
可用的自定义特性(查看
app/Traits/Filament/
获取全部):
  • CanModifyResource
    - 关联管理器、自定义操作
  • CanCustomizePage
    - 小部件、头部操作
  • CanModifyForm
    /
    CanModifyTable
    - 表单/表格钩子

Plugin Settings

插件设置

Implement
HasPluginSettings
on your main class:
php
use App\Contracts\Plugins\HasPluginSettings;
use App\Traits\EnvironmentWriterTrait;
use Filament\Forms\Components\TextInput;
use Filament\Notifications\Notification;

class MyPlugin implements Plugin, HasPluginSettings
{
    use EnvironmentWriterTrait;

    public function getSettingsForm(): array
    {
        return [
            TextInput::make('api_key')
                ->required()
                ->default(fn () => config('my-plugin.api_key')),
        ];
    }

    public function saveSettings(array $data): void
    {
        $this->writeToEnvironment([
            'MY_PLUGIN_API_KEY' => $data['api_key'],
        ]);

        Notification::make()->title('Settings saved')->success()->send();
    }
}
Always prefix env vars with your plugin ID to avoid conflicts.
在主类中实现
HasPluginSettings
接口:
php
use App\Contracts\Plugins\HasPluginSettings;
use App\Traits\EnvironmentWriterTrait;
use Filament\Forms\Components\TextInput;
use Filament\Notifications\Notification;

class MyPlugin implements Plugin, HasPluginSettings
{
    use EnvironmentWriterTrait;

    public function getSettingsForm(): array
    {
        return [
            TextInput::make('api_key')
                ->required()
                ->default(fn () => config('my-plugin.api_key')),
        ];
    }

    public function saveSettings(array $data): void
    {
        $this->writeToEnvironment([
            'MY_PLUGIN_API_KEY' => $data['api_key'],
        ]);

        Notification::make()->title('Settings saved')->success()->send();
    }
}
环境变量请始终以插件ID作为前缀,避免冲突。

Permissions

权限

Admin Role Permissions

管理员角色权限

In your service provider's
register()
:
php
use App\Models\Role;

// Shorthand: registers viewList, view, create, update, delete
Role::registerCustomDefaultPermissions('myModel');

// Custom permissions
Role::registerCustomPermissions([
    'myModel' => ['export', 'approve'],
    'server'  => ['customAction'], // extend existing model
]);

// Optional: icon for permission group
Role::registerCustomModelIcon('myModel', 'tabler-star');
在服务提供者的
register()
方法中:
php
use App\Models\Role;

// 简写方式:注册viewList、view、create、update、delete权限
Role::registerCustomDefaultPermissions('myModel');

// 自定义权限
Role::registerCustomPermissions([
    'myModel' => ['export', 'approve'],
    'server'  => ['customAction'], // 扩展现有模型
]);

// 可选:为权限组设置图标
Role::registerCustomModelIcon('myModel', 'tabler-star');

Subuser Permissions

子用户权限

php
use App\Models\Subuser;

// New permission group
Subuser::registerCustomPermissions('myFeature', ['read', 'write'], 'tabler-bolt', false);

// Append to existing group
Subuser::registerCustomPermissions('console', ['myCustomAction']);
php
use App\Models\Subuser;

// 新增权限组
Subuser::registerCustomPermissions('myFeature', ['read', 'write'], 'tabler-bolt', false);

// 添加到现有权限组
Subuser::registerCustomPermissions('console', ['myCustomAction']);

Routes

路由

Create a
RouteServiceProvider
in
src/Providers/
:
php
use Illuminate\Foundation\Support\Providers\RouteServiceProvider;
use Illuminate\Support\Facades\Route;

class MyPluginRoutesProvider extends RouteServiceProvider
{
    public function boot(): void
    {
        $this->routes(function () {
            // Simple route
            Route::get('/my-plugin/test', [TestController::class, 'index'])
                ->name('my-plugin.test');

            // Load from file
            Route::prefix('/my-plugin')
                ->group(plugin_path('my-plugin', 'routes/web.php'));

            // Append to client API
            Route::middleware(['api', 'client-api', 'throttle:api.client'])
                ->prefix('/api/client/servers/{server}')
                ->scopeBindings()
                ->group(plugin_path('my-plugin', 'routes/api-client.php'));
        });
    }
}
src/Providers/
目录下创建
RouteServiceProvider
php
use Illuminate\Foundation\Support\Providers\RouteServiceProvider;
use Illuminate\Support\Facades\Route;

class MyPluginRoutesProvider extends RouteServiceProvider
{
    public function boot(): void
    {
        $this->routes(function () {
            // 简单路由
            Route::get('/my-plugin/test', [TestController::class, 'index'])
                ->name('my-plugin.test');

            // 从文件加载路由
            Route::prefix('/my-plugin')
                ->group(plugin_path('my-plugin', 'routes/web.php'));

            // 添加到客户端API
            Route::middleware(['api', 'client-api', 'throttle:api.client'])
                ->prefix('/api/client/servers/{server}')
                ->scopeBindings()
                ->group(plugin_path('my-plugin', 'routes/api-client.php'));
        });
    }
}

Models & Relationships

模型与关联

Add Relationship to Core Models

为核心模型添加关联

In your service provider's
boot()
:
php
use App\Models\Server;
use MyPlugin\Models\Ticket;

public function boot(): void
{
    Server::resolveRelationUsing('tickets', fn (Server $server) => 
        $server->hasMany(Ticket::class, 'server_id', 'id')
    );
}
Now
$server->tickets
works everywhere.
在服务提供者的
boot()
方法中:
php
use App\Models\Server;
use MyPlugin\Models\Ticket;

public function boot(): void
{
    Server::resolveRelationUsing('tickets', fn (Server $server) => 
        $server->hasMany(Ticket::class, 'server_id', 'id')
    );
}
现在
$server->tickets
可在任何地方使用。

Policies

策略

php
use App\Policies\DefaultAdminPolicies;

class MyModelPolicy
{
    use DefaultAdminPolicies;
    
    protected string $modelName = 'myModel';
}
This automatically checks admin role permissions based on the registered model name.
php
use App\Policies\DefaultAdminPolicies;

class MyModelPolicy
{
    use DefaultAdminPolicies;
    
    protected string $modelName = 'myModel';
}
这会基于已注册的模型名称自动检查管理员角色权限。

Translations

翻译

Place in
lang/{locale}/
(e.g.,
lang/en/strings.php
):
php
return [
    'welcome' => 'Welcome',
    'item' => 'Item|Items', // Pluralization
];
Usage:
php
trans('my-plugin::strings.welcome')
trans_choice('my-plugin::strings.item', 2) // "Items"
将翻译文件放置在
lang/{locale}/
目录下(例如:
lang/en/strings.php
):
php
return [
    'welcome' => 'Welcome',
    'item' => 'Item|Items', // 复数形式
];
使用方式:
php
trans('my-plugin::strings.welcome')
trans_choice('my-plugin::strings.item', 2) // "Items"

Views

视图

Place in
resources/views/
:
php
view('my-plugin::my-view')
// → plugins/my-plugin/resources/views/my-view.blade.php
将视图文件放置在
resources/views/
目录下:
php
view('my-plugin::my-view')
// → plugins/my-plugin/resources/views/my-view.blade.php

Common Patterns

常见模式

FilamentPHP Components

FilamentPHP组件

See FilamentPHP Patterns for:
  • Resources (CRUD interfaces)
  • Pages (custom pages)
  • Widgets (dashboard components)
  • Relation Managers (manage related records)
  • Custom Actions (reusable buttons)
  • Form components (inputs, selects, toggles)
  • Table columns and filters
查看FilamentPHP模式了解:
  • 资源(CRUD界面)
  • 页面(自定义页面)
  • 小部件(仪表盘组件)
  • 关联管理器(管理关联记录)
  • 自定义操作(可复用按钮)
  • 表单组件(输入框、选择器、开关)
  • 表格列与过滤器

Advanced Patterns

进阶模式

See Advanced Patterns for:
  • Model events and hooks
  • Enums with Filament interfaces
  • Service classes
  • HTTP controllers and API routes
  • Artisan commands and scheduling
  • HTTP macros for external APIs
  • Database migrations
  • Error handling
查看进阶模式了解:
  • 模型事件与钩子
  • 支持Filament接口的枚举
  • 服务类
  • HTTP控制器与API路由
  • Artisan命令与调度
  • 用于外部API的HTTP宏
  • 数据库迁移
  • 错误处理

Complete Example

完整示例

See Complete Plugin Walkthrough for a step-by-step guide building a "Server Notes" plugin.
查看完整插件开发教程,了解分步构建“服务器笔记”插件的过程。

Publishing a Plugin

发布插件

  1. Open
    plugin.json
    and remove the
    meta
    block (internal use only)
  2. Zip the entire plugin folder
  3. Share the zip — users install via panel UI Import button or manually drop into
    plugins/
Publish to the community:
  1. 打开
    plugin.json
    移除
    meta
    块(仅内部使用)
  2. 将整个插件文件夹压缩为ZIP包
  3. 分享ZIP包——用户可通过面板UI的导入按钮安装,或手动将其放入
    plugins/
    目录
发布到社区:

Tips & Gotchas

提示与注意事项

  • Namespace in plugin.json: Use
    \\
    (double backslash) for namespace separators
  • Migration naming: Use numeric prefixes (
    001_
    ,
    002_
    ) to control execution order
  • Environment variables: Always prefix with your plugin ID (e.g.,
    MY_PLUGIN_*
    )
  • Panel context: Use
    Filament::getTenant()
    to get current server in server panel
  • Auto-discovery: Service providers, commands, migrations, and policies are auto-discovered
  • Relation managers: Must be registered on core resources via
    registerCustomRelations()
    in service provider's
    register()
    method
  • Testing: Use
    php artisan migrate:fresh --seed
    to reset and test migrations
  • Permissions: Register in service provider's
    register()
    , not
    boot()
  • plugin.json中的命名空间:使用
    \\
    (双反斜杠)作为命名空间分隔符
  • 迁移文件命名:使用数字前缀(
    001_
    002_
    )控制执行顺序
  • 环境变量:始终以插件ID作为前缀(例如:
    MY_PLUGIN_*
  • 面板上下文:在服务器面板中使用
    Filament::getTenant()
    获取当前服务器
  • 自动发现:服务提供者、命令、迁移文件和策略会被自动发现
  • 关联管理器:必须在服务提供者的
    register()
    方法中通过
    registerCustomRelations()
    注册到核心资源
  • 测试:使用
    php artisan migrate:fresh --seed
    重置并测试迁移
  • 权限:在服务提供者的
    register()
    方法中注册,而非
    boot()

Getting Help

获取帮助