Skip to content

Powerful, fast and developer-friendly extension framework for Pterodactyl.

License

Notifications You must be signed in to change notification settings

ReshapedFramework/framework

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

24 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Reshaped Framework

A powerful, developer-friendly extension framework for Pterodactyl Panel


Why Reshaped?

Feature Blueprint Reshaped
Manifest format Custom YAML (flat) Rich YAML with full schema validation
CLI Bash script TypeScript CLI with autocomplete
Validation Basic Zod schema + file existence checks
PHP API Global function calls Injectable ExtensionLibrary class
Data storage Key-value (string only) Typed key-value (bool, int, float, JSON)
Per-server data
Per-user data
Caching API
Events/listeners
Notifications API Basic User notifications + flash messages
Permission system ✅ (extends Pterodactyl perms)
React hook system Slot files <ReshapedSlot> component + registry
Error boundaries ✅ Crashes isolated per extension
Dev panel ✅ Visual debug overlay
Extension settings UI ❌ (manual) ✅ Auto-generated from manifest
Rollback on failure ✅ Automatic backup + restore
Audit logging
Dev mode (symlink) reshaped install --dev

Installation

Requirements

  • Pterodactyl Panel 1.11+
  • PHP 8.1+
  • Node.js 20+
  • Composer

Install the framework

# In your Pterodactyl root
composer require reshapedframework/framework

# Add to config/app.php providers array:
# Pterodactyl\ReshapedFramework\Providers\ReshapedServiceProvider::class

# Run migrations
php artisan migrate

# Install the CLI globally
npm install -g reshaped-cli

Building Extensions

1. Scaffold a new extension

reshaped init my-extension
# or with a template:
reshaped init my-extension --template full

Available templates:

  • basic — Simple admin page
  • admin — Full admin panel with auto-generated settings UI
  • client — Client panel tabs and hooks
  • api — API-only, no frontend
  • full — Everything: admin + client + API + migrations

2. Extension structure

my-extension/
├── reshaped.yml          # Manifest (schema-validated)
├── README.md
├── src/
│   ├── php/
│   │   ├── Controller.php
│   │   └── ServiceProvider.php
│   └── hooks/            # React components injected into panel slots
│       └── AfterTerminal.tsx
├── views/
│   ├── admin/
│   │   └── index.blade.php
│   └── client/
│       └── index.blade.php
├── routes/
│   ├── api.php
│   ├── admin.php
│   └── client.php
├── database/
│   └── migrations/
│       └── 2024_01_01_create_my_extension_table.php
├── config/
│   └── my-extension.php
├── assets/
│   └── logo.png
└── scripts/
    ├── install.sh
    └── uninstall.sh

3. The manifest (reshaped.yml)

meta:
  id: my-extension
  name: My Extension
  version: 1.0.0
  description: Does something awesome
  author: yourusername
  license: MIT

compat:
  reshaped: ">=1.0.0"
  pterodactyl: ">=1.11.0"

provides:
  admin:
    views: [index.blade.php]
    nav:
      - label: My Extension
        icon: fas fa-puzzle-piece
        route: my-extension
  api: routes/api.php
  migrations: true

hooks:
  Server/Terminal/AfterContent:
    component: hooks/AfterTerminal
    priority: 10

settings:
  - key: enabled
    label: Enable Extension
    type: boolean
    default: true
  - key: api_url
    label: API URL
    type: url
    validation: "required|url"

permissions:
  - key: my-extension.use
    description: Use the extension
    default: allow

4. Using the PHP library

<?php

use Pterodactyl\ReshapedFramework\Libraries\ExtensionLibrary;

class MyController extends Controller
{
    public function __construct(private ExtensionLibrary $reshaped) {}

    public function index()
    {
        // Settings
        $enabled = $this->reshaped->setting('my-extension', 'enabled', true);
        $this->reshaped->setSetting('my-extension', 'api_url', 'https://example.com');

        // Per-server data
        $data = $this->reshaped->serverData('my-extension', $server->id, 'last_run');
        $this->reshaped->setServerData('my-extension', $server->id, 'last_run', now());

        // Per-user data
        $pref = $this->reshaped->userData('my-extension', $user->id, 'preference');

        // Caching
        $result = $this->reshaped->remember('my-extension', 'expensive_query', 3600, function() {
            return DB::table('something')->get();
        });

        // Notifications
        $this->reshaped->notify('Settings saved!', 'success');
        $this->reshaped->notifyUser($user->id, 'Alert', 'Something happened', 'warning');

        // Events
        $this->reshaped->fire('my-extension', 'data.updated', ['key' => 'value']);

        // Check other extensions
        if ($this->reshaped->isInstalled('other-extension')) {
            // Cross-extension compatibility
        }

        // Logging
        $this->reshaped->log('my-extension', 'User performed action', 'info', [
            'user_id' => $user->id,
        ]);
    }
}

5. React hooks

Inject components into any panel slot:

// src/hooks/AfterTerminal.tsx
import React from 'react';
import { useStoreState } from 'easy-peasy';
import { useExtensionSettings } from '@/reshaped/hooks';

export default function AfterTerminal() {
    const server = useStoreState(state => state.server.data);
    const settings = useExtensionSettings('my-extension');

    if (!settings?.enabled || !server) return null;

    return (
        <div className="mt-4 p-4 bg-gray-700 rounded">
            <h3 className="text-white">My Extension</h3>
            <p className="text-gray-400">Server: {server.name}</p>
        </div>
    );
}

Add the slot to your panel's Blade/React where you want injections:

import { ReshapedSlot } from '@/reshaped/hooks';

// In ServerConsoleContainer.tsx:
<ReshapedSlot name="Server/Terminal/AfterContent" serverId={server.id} />

Available slots: see docs/slots.md

6. Auto-generated settings UI

Extensions with a settings array in their manifest automatically get a settings page at /admin/extensions/{id}. No extra code required — just include this in your admin view:

@include('reshaped::admin.settings', ['extension' => 'my-extension'])

7. Install and test

# Development mode (symlinks, no copy)
reshaped install --dev /path/to/my-extension

# Validate before install
reshaped validate /path/to/my-extension

# Build with hot reload
reshaped build --watch

# List installed
reshaped list

# Remove
reshaped remove my-extension

CLI Reference

reshaped install <file.rsx>      Install an extension
reshaped install --dev <dir>     Install in dev mode
reshaped remove <id>             Remove an extension
reshaped init [dir]              Scaffold a new extension
reshaped build                   Build panel assets
reshaped build --watch           Build with hot reload
reshaped dev                     Dev mode with HMR
reshaped export <id>             Package to .rsx file
reshaped list                    List installed extensions
reshaped info <id>               Show extension details
reshaped validate <dir>          Validate extension
reshaped debug check             Check dependencies
reshaped debug logs              View debug log
reshaped upgrade                 Upgrade Reshaped

Blade Directives

@reshapedSlot('Server/Terminal/AfterContent')
@reshapedAsset('my-extension', 'script.js')
@reshapedSetting('my-extension', 'api_url')
@ifExtension('my-extension') ... @endIfExtension
@reshapedPermission('my-extension', 'use') ... @endReshapedPermission

Slots Reference

See docs/slots.md for the full list of available injection points.


License

Licesend under MIT. Check LICENSE for more information