- TypeScript 37.8%
- PHP 36.3%
- Blade 14.2%
- CSS 10.6%
- JavaScript 0.7%
- Other 0.4%
| config | ||
| database/migrations | ||
| private | ||
| public | ||
| resources | ||
| routes | ||
| screenshots | ||
| src | ||
| .gitignore | ||
| admin.css | ||
| admin.wrapper.blade.php | ||
| CHANGELOG.md | ||
| composer.json | ||
| conf.yml | ||
| CONTRIBUTING.md | ||
| dashboard.wrapper.blade.php | ||
| LICENSE | ||
| package.json | ||
| postcss.config.js | ||
| README.md | ||
| root.css | ||
| SECURITY.md | ||
| tailwind.config.js | ||
| tsconfig.json | ||
| view.blade.php | ||
| webpack.config.js | ||
🎨 Vertex Theme — A Modern Pterodactyl Panel Theme
Version: 1.0.0
Status: ✅ Stable
Vertex is a feature-rich theme package for the Pterodactyl game panel that adds dark mode, drag-and-drop server sorting, a live color editor, floating windows, a plugin installer with live marketplace APIs, announcement management, and more — all without modifying any core Pterodactyl files.
✨ v6.0 Complete UI/UX Redesign
Vertex v6.0 brings a complete visual overhaul inspired by premium design systems (uupm.cc, arix.gg):
Glass Hierarchy (4 Tiers):
glass-heavy(92% opacity / 32px blur) — modals, overlaysglass(85% / 24px blur) — primary cards, panelsglass-light(70% / 16px blur) — nav bars, sub-panelsglass-subtle(50% / 12px blur) — tabs, inline elements
Background System: 5 animated radial gradient blobs + noise texture + scanline overlay for glass depth
Typography: Gradient display headings, letter-spacing hierarchy, gradient text utilities
Buttons: Gradient primary with glow, asymmetric corners, 3 size variants, icon buttons, loading states
Card Variants: Stat cards (icon + metric + trend), feature cards, plugin cards (name + rating + install), server cards (status + resources), image cards, gradient border cards
Navigation: Icon+text items, section headers, collapsible trees, gradient active indicators, badges, sidebar search
Input System: Glass inputs, icon wrappers, animated focus glow, floating labels, custom selects, toggles, checkboxes
Progress & Metrics: Gradient progress bars with glow, large gradient metric numbers
Micro-interactions: Skeleton loading, hover glow intensification, animated gradient borders, fade-in/slide-in animations, pulsing status indicators
Dark Mode
| Component | Preview |
|---|---|
| Full Page | ![]() |
| Glass Hierarchy | ![]() |
| Typography | ![]() |
| Buttons | ![]() |
| Cards | ![]() |
| Navigation | ![]() |
| Inputs | ![]() |
| Progress & Metrics | ![]() |
| Micro-interactions | ![]() |
| Announcements | ![]() |
Light Mode
| Component | Preview |
|---|---|
| Full Page | ![]() |
| Glass Hierarchy | ![]() |
| Typography | ![]() |
| Buttons | ![]() |
| Cards | ![]() |
| Navigation | ![]() |
| Inputs | ![]() |
| Progress & Metrics | ![]() |
| Micro-interactions | ![]() |
| Announcements | ![]() |
Mobile & Interaction States
| Component | Preview |
|---|---|
| Mobile Full (Dark) | ![]() |
| Mobile Cards (Dark) | ![]() |
| Hover Glass Card | ![]() |
| Hover Primary Button | ![]() |
| Focus Input | ![]() |
| Hover Gradient Border | ![]() |
📸 Screenshots
| Plugin Installer | Color Editor | Floating Windows |
|---|---|---|
![]() |
![]() |
![]() |
| Announcements | Properties Manager | Subdomain Manager |
|---|---|---|
![]() |
![]() |
![]() |
🧩 Blueprint Installation
Vertex can be installed via Blueprint, the Pterodactyl extension framework.
One-Click Install
cd /var/www/pterodactyl
blueprint -install vertex
This installs the Vertex CSS theme (glass hierarchy, dark/light mode, all visual components). The theme will be active immediately on your client dashboard and admin panel.
Full Feature Install (Composer)
For access to ALL Vertex features — live color editor, announcements, plugin installer, floating windows, subdomain manager and more — install the full Composer package:
cd /var/www/pterodactyl
composer require crashmedia/vertex-theme
php artisan vertex:install
php artisan migrate
php artisan optimize:clear
Then visit /admin/vertex to configure the theme.
Requirements
| Requirement | Blueprint (CSS) | Full Package |
|---|---|---|
| Pterodactyl 1.12.2+ | ✅ | ✅ |
| Blueprint beta-2025-09+ | ✅ | — |
| PHP 8.2+ | — | ✅ |
| Composer 2+ | — | ✅ |
| Node.js 20+ | — | ✅ |
| Redis (recommended) | — | ✅ |
What Blueprint Installs
- root.css — Color palette and CSS custom properties (bundled with React)
- vertex.css — Full glass-morphism theme (1,620 lines)
- vertex-dark.css — Dark mode overrides
- vertex-admin.css — Admin panel styling
- vertex-floating.css — Floating window styles
- Scanline + noise overlays — Glass depth effects
What Full Package Adds
- Live HSL color editor with 12 presets
- Announcement system with scheduling
- Drag-and-drop server sorting
- Floating window multitasking
- Plugin installer (4 marketplaces)
- Minecraft version changer
- Subdomain manager (Cloudflare DNS)
- Icon changer, properties manager, modpack installer
- 5 layout variants, 4 login screen variants
- PWA support, Cloudflare Turnstile
- Custom email templates
- 10 language translations
Features
| Feature | Status |
|---|---|
| v6.0 Glass UI Redesign | ✅ Stable |
| Dark/Light Mode Toggle | ✅ Stable |
| Live Color Editor (HSL presets) | ✅ Stable |
| Drag-and-Drop Server Sorting | ✅ Stable |
| Floating Window Multitasking | ✅ Stable |
| Announcement Manager | ✅ Stable |
| Privacy Mode (IP/data blurring) | ✅ Stable |
| Multi-Language Support (5 languages) | ⚠️ Partial |
| Minecraft Version Changer | ✅ Stable |
| Plugin Installer (4 marketplaces) | ✅ Stable |
| Subdomain Manager | ⚠️ Partial |
| Icon Changer | ⚠️ Partial |
| Custom CSS Injection | ✅ Stable |
| Custom Logo Upload | ✅ Stable |
| 5 Layout CSS Classes | ✅ Stable |
| PWA Support | 📋 Planned |
| Cloudflare Turnstile | 📋 Planned |
| Page Builder | 📋 Planned |
Feature Details
Color Editor ✅ Stable
What it does: A live color editor that lets administrators customize the entire theme palette from the admin panel. Choose a primary color and the system generates a full HSL-shaded palette (50–900). Changes preview live before applying.
Presets: 12 named presets are available — blue, purple, emerald, rose, amber, cyan, indigo, pink, teal, violet, slate, and orange.
Implementation: ColorService::generatePalette() takes a hex color and produces
10 HSL-derived shades. The palette is rendered as CSS custom properties on :root.
ColorPresetService provides the 12 named presets. The editor UI is in
ColorEditor.tsx with preview/apply/preset API endpoints.
API endpoints: POST /api/client/vertex/editor/preview,
POST /api/client/vertex/editor/apply,
POST /api/client/vertex/editor/preset/{name}
Files: src/Services/ColorService.php,
src/Services/ColorPresetService.php,
resources/scripts/vertex/components/ColorEditor.tsx
Announcements ✅ Stable
What it does: A full CRUD announcement system with scheduling. Administrators can create announcements that appear as banners across the panel. Each announcement supports:
- Scheduling —
starts_atandends_attimestamps for time-boxed announcements - Type badges —
info,warning,success,errorwith color-coded styling - Dismissible — Users can dismiss announcements (stored in session)
- Active/inactive — Toggle without deleting
- Dark/light mode — Automatically adapts to the current theme mode
Implementation: The Announcement model uses starts_at and ends_at to
determine active status. The AnnouncementBanner.tsx component renders banners.
AnnouncementProvider.tsx provides announcements via React Context.
Files: src/Models/Announcement.php,
src/Http/Controllers/AnnouncementController.php,
resources/scripts/vertex/components/AnnouncementBanner.tsx,
resources/scripts/vertex/providers/AnnouncementProvider.tsx
Server Sorting ✅ Stable
What it does: Drag-and-drop reordering of servers on the client dashboard. Supports 5 sort methods:
- Custom — Drag servers into any order
- Alphabetical (A-Z) — By server name
- Alphabetical (Z-A) — Reverse alphabetical
- Date Created — Newest first
- By Status — Online servers first, then offline, suspended, installing
The sort order is persisted per-user in the server_sort_preferences database
table. The API (at GET/POST /api/client/vertex/servers/sort) stores an ordered
array of server IDs and the selected sort method.
Implementation: ServerSorter.tsx uses DndContext, SortableContext,
KeyboardSensor, and PointerSensor from @dnd-kit. Each server is rendered as a
SortableItem with a drag handle. The useServerSort hook handles API persistence
and optimistic UI updates.
Files: src/Http/Controllers/ServerSortController.php,
src/Models/ServerSortPreference.php,
resources/scripts/vertex/components/ServerSorter.tsx,
resources/scripts/vertex/components/SortableItem.tsx,
resources/scripts/vertex/hooks/useServerSort.ts,
database/migrations/2026_05_20_000003_create_server_sort_preferences_table.php
Floating Windows ✅ Stable
What it does: Right-clicking any server opens a context menu
(ServerContextMenu.tsx) with options to open console, files, databases, and
settings in floating windows. Each window is:
- Draggable — Click and drag the title bar to reposition
- Resizable — Drag the corner/edge handles
- Minimizable — Collapses to the floating taskbar
- Stackable — Z-index management brings focused windows to front
- Mobile responsive — Windows adapt to smaller viewports
Implementation: FloatingWindowManager.tsx provides a React context
(FloatingWindowContext) that tracks all open windows with their position, size,
z-index, minimize state, and content type. The FloatingWindow component renders
each window at its tracked position. The FloatingTaskbar shows minimized windows.
Each new window cascades its position by 32px offsets.
Windows support these content types: console, files, settings, terminal,
database, schedules. Default size is 800×520px, minimum is 400×300px.
Z-index starts at 100 and increments per new window. The feature is gated by the
floating_windows config flag.
Files: resources/scripts/vertex/components/FloatingWindowManager.tsx,
resources/scripts/vertex/components/FloatingWindow.tsx,
resources/scripts/vertex/components/FloatingTaskbar.tsx,
resources/scripts/vertex/components/ServerContextMenu.tsx,
resources/scripts/vertex/hooks/useServerContextMenu.ts,
resources/css/vertex-floating.css
Minecraft Version Changer ✅ Stable
What it does: Fetches the Minecraft version manifest from
https://launchermeta.mojang.com/mc/game/version_manifest_v2.json and caches it for
1 hour. Versions are categorized by type (release, snapshot, beta, alpha). When a
user selects a version, the system updates the Pterodactyl egg variable (default
variable name: MC_VERSION, configurable via config/vertex.php →
minecraft_version_variable). An optional reinstall can be triggered.
Fallback: If the Mojang API is unreachable, a hardcoded list of 15 release
versions and 3 snapshot versions is used (defined in VersionService.php as
FALLBACK_VERSIONS).
Implementation: VersionService::changeVersion() validates the requested version
against the manifest, updates the egg variable via Pterodactyl's Eloquent relations,
and optionally calls $server->reinstall(). The $server->eggVariables() relation
is filtered by the configured variable name.
Files: src/Services/VersionService.php,
src/Http/Controllers/VersionChangerController.php,
resources/scripts/vertex/components/VersionChanger.tsx,
config/vertex.php (minecraft_version_variable)
Properties Manager ✅ Stable
What it does: Provides a visual editor for server.properties on Minecraft
servers. It reads the current properties file via the Wings daemon API and presents
them in categorized tabs. Each property definition includes:
- key — The
server.propertieskey (e.g.,gamemode) - type —
string,boolean,enum, orint - default — Minecraft's default value
- category — Tab grouping (World, Gameplay, Network, Performance, Chat, Misc)
- docUrl — Link to the Minecraft Wiki documentation page
- description — Human-readable explanation
The frontend renders type-specific inputs:
- Boolean → toggle switch with green indicator
- Enum → dropdown select
- Int → number field with increment/decrement buttons
- String → text input
Changed properties that require a restart (port, level-name, online-mode, etc.) are
flagged with a warning. A restart-required list of 18 property keys is maintained in
PropertiesController::requiresRestart().
Implementation: PropertiesService::getProperties() fetches the current
server.properties file from the Wings daemon HTTP API. getPropertyDefinitions()
returns a 55+ item array of property metadata. updateProperties() writes changes
back via the Wings API. The controller checks server ownership via the
ChecksServerAccess trait and verifies the server is online before reading.
Files: src/Services/PropertiesService.php,
src/Http/Controllers/PropertiesController.php,
resources/scripts/vertex/components/PropertiesManager.tsx
5 Layout CSS Classes ✅ Stable
The following layout names are applied as CSS classes on the <body> element
(e.g., <body class="vertex-layout-sidebar-right">). The InjectThemeSettings
middleware reads the active layout from the theme settings and shares it with
Blade views via View::share('vertexConfig', $config).
| Config Key | CSS Class | Description |
|---|---|---|
default |
vertex-layout-default |
Sidebar on the left (AdminLTE default) |
sidebar-right |
vertex-layout-sidebar-right |
Sidebar on the right |
topnav |
vertex-layout-topnav |
Top navigation bar, no sidebar |
compact |
vertex-layout-compact |
Compact view with reduced padding |
combined |
vertex-layout-combined |
Sidebar + horizontal top navigation |
Note: The CSS classes are applied, but the actual CSS rules for each layout
are still being refined in vertex.css. The class names serve as hooks for theme
styling.
Subdomain Manager ⚠️ Partial
What it does: Full CRUD for subdomain records linked to server allocations. Each subdomain has:
- subdomain — The subdomain label (e.g.,
mc) - domain — The parent domain (e.g.,
crashmedia.ca) - allocation_id — Links to a Pterodactyl server allocation (IP:port)
- record_type —
A,CNAME, orANAME - target — The DNS target value
- ssl — Boolean flag for SSL (not automated yet)
- active — Enable/disable toggle
What's missing (⚠️ Partial):
- DNS API integration — Records are stored in the database but are NOT automatically created on external DNS providers (Cloudflare, Route53, etc.). Operators must create the DNS records manually.
- SSL automation — The
sslflag exists on the model but there is no ACME integration (Let's Encrypt, Certbot, etc.). - Unique constraint bug — The unique constraint on the
subdomaincolumn is global across all domains. It should be scoped per-domain (e.g.,mc.example.comandmc.mydomain.comshould both be valid). The validation rule inSubdomainController::store()attempts to do a composite unique check but may not be fully correct.
Files: src/Http/Controllers/SubdomainController.php,
src/Models/Subdomain.php,
resources/scripts/vertex/components/SubdomainManager.tsx,
database/migrations/2026_05_20_000010_create_subdomains_table.php,
routes/api.php
Icon Changer ⚠️ Partial
What it does: Allows users to upload a custom PNG icon for their Minecraft server. The icon is:
- Validated as PNG (MIME type check)
- Limited to 2MB maximum
- Resized to 64×64 pixels using Intervention/Image
- Stored in Laravel's filesystem (
Storage::put()) - Served via the
Storage::url()helper - The previous icon is removed when a new one is uploaded
The frontend (IconChanger.tsx) provides a drag-and-drop upload zone.
What's missing (⚠️ Partial):
- Missing Composer dependency — The code in
IconService.phpimportsIntervention\Image\ImageManagerandIntervention\Image\Drivers\Gd\Driver, butcomposer.jsondoes NOT declareintervention/imageas a dependency. This meanscomposer installwill not pull in the required library, and the icon upload will fail at runtime. The fix is to add"intervention/image": "^3.0"tocomposer.jsonand runcomposer update.
Files: src/Http/Controllers/IconController.php,
src/Services/IconService.php,
src/Models/ServerIcon.php,
resources/scripts/vertex/components/IconChanger.tsx,
database/migrations/2026_05_20_000013_create_server_icons_table.php
Multi-Language Support ⚠️ Partial
What it does: Provides translatable strings for the theme. English is fully
translated with both messages.php (flash messages, API responses) and
theme.php (UI labels like "Dark Mode", "Privacy Mode", etc.).
Language status:
| Language | messages.php |
theme.php (UI labels) |
|---|---|---|
| English (en) | ✅ | ✅ |
| Dutch (nl) | ✅ | ❌ |
| German (de) | ✅ | ❌ |
| French (fr) | ✅ | ❌ |
| Spanish (es) | ✅ | ❌ |
What's missing (⚠️ Partial):
- Dutch, German, French, and Spanish have translated flash/API messages but the
theme.phpfile with UI labels (Dark Mode, Light Mode, Privacy Mode, Server Sort methods, etc.) does not exist for these languages. These labels fall back to English.
Files:
resources/lang/en/messages.php+resources/lang/en/theme.phpresources/lang/nl/messages.php(only)resources/lang/de/messages.php(only)resources/lang/fr/messages.php(only)resources/lang/es/messages.php(only)
Plugin Installer ✅ Stable
What it does: A fully integrated plugin marketplace that connects to four live APIs to let users browse, search, install, and uninstall plugins directly from the Pterodactyl panel — no manual SFTP needed.
Marketplace Connections
| Marketplace | API Endpoint | Auth Required |
|---|---|---|
| Modrinth | https://api.modrinth.com/v2 |
None |
| Hangar | https://hangar.papermc.io/api/v1 |
None |
| SpigotMC | https://api.spiget.org/v2 (community wrapper) |
None |
| CurseForge | https://api.curseforge.com/v1 |
CURSEFORGE_API_KEY in .env |
Search and browsing: The frontend (PluginInstaller.tsx) presents a tabbed
marketplace switcher. Users can search across any of the four connected
marketplaces. Search results show the plugin name, author, description, rating,
download count, and a one-click install button.
Install and uninstall: Installation uses the real Pterodactyl Wings daemon API to:
- Download the plugin JAR from the selected marketplace
- Upload the JAR to the server's
plugins/directory via the Wings file API - Create an
InstalledPlugindatabase record to track the installation
Uninstallation removes the JAR file from the server via the Wings daemon file delete API and removes the database record.
CurseForge setup: To enable CurseForge browsing, add your API key to the
panel's .env file:
CURSEFORGE_API_KEY=$2a$10$your-key-here
Modrinth, Hangar, and SpigotMC work out of the box with no configuration needed.
Implementation: PluginMarketplaceService.php handles API communication
with all four marketplaces. PluginController.php manages search, install,
uninstall, and toggle operations, communicating with the Wings daemon for file
operations. The frontend is in PluginInstaller.tsx.
Files: src/Services/PluginMarketplaceService.php,
src/Http/Controllers/PluginController.php,
src/Models/InstalledPlugin.php,
resources/scripts/vertex/components/PluginInstaller.tsx,
database/migrations/2026_05_20_000012_create_installed_plugins_table.php
📋 Planned Features (Config Flags Only)
These features have configuration keys in config/vertex.php but no backend or
frontend implementation beyond the flag itself.
| Feature | Config Key | Current State |
|---|---|---|
| Login Screen Variants | login_layouts (4 options: centered card, split screen, fullscreen, minimal) |
Config exists; Blade template variants not built |
| Email Templates | — (no dedicated flag) | Not started |
| Font & Icon Picker | font_family default exists |
UI picker not built; the default font is applied via CSS variable |
| File Manager Enhancements | — (no dedicated flag) | Folder upload and Monaco editor integration not started |
| PWA Support | pwa_support (default: false) |
Config flag exists; manifest.json and service worker not written |
| Cloudflare Turnstile | turnstile (default: false) |
Config flag exists; Turnstile widget integration not built |
| Export & Import | — (no dedicated flag) | Settings backup/restore not started |
| Page Builder | page_builder (default: true) |
Config flag exists but only server sorting uses dnd-kit; the dashboard widget builder is not implemented |
Full API Reference
Base URL: /api/client/vertex
All client endpoints require authentication (auth middleware) and are throttled
at 60 requests per minute. Server-scoped endpoints additionally verify ownership
via the ChecksServerAccess trait.
Theme Configuration
| Method | Endpoint | Description |
|---|---|---|
GET |
/theme |
Get current theme config, palette, feature flags, and user preferences |
POST |
/editor/preview |
Generate a color palette preview without saving |
POST |
/editor/apply |
Apply color changes to the database |
POST |
/editor/preset/{name} |
Apply a named preset (blue, purple, emerald, rose, amber, cyan, indigo, pink, teal, violet, slate, orange) |
Request body for /editor/preview and /editor/apply:
{
"primary_color": "#2563eb",
"background_color": "#f8fafc",
"surface_color": "#ffffff",
"text_color": "#0f172a"
}
Response from /editor/preview:
{
"palette": { "50": "#eff6ff", "100": "#dbeafe", ..., "900": "#1e3a5f" },
"css_vars": { "--vertex-primary-50": "#eff6ff", ... },
"contrast": "#ffffff",
"preview": { "primary": "#2563eb", "background": "#f8fafc", "surface": "#ffffff", "text": "#0f172a" }
}
Public Endpoint (no auth)
| Method | Endpoint | Description |
|---|---|---|
GET |
/api/vertex/public/theme |
Public theme config for login/register pages |
Announcements
| Method | Endpoint | Description |
|---|---|---|
GET |
/announcements |
Get all currently-active announcements |
POST |
/announcements/{id}/dismiss |
Dismiss an announcement (stored in session) |
Server Sorting
| Method | Endpoint | Description |
|---|---|---|
GET |
/servers/sort |
Get the current user's sort preference |
POST |
/servers/sort |
Save a sort preference |
Request body for POST /servers/sort:
{
"sort_order": [5, 2, 8, 1, 3],
"sort_method": "custom"
}
Subdomain Manager (Addon 1)
| Method | Endpoint | Auth | Description |
|---|---|---|---|
GET |
/servers/{server}/subdomains |
Owner | List subdomains for a server |
POST |
/servers/{server}/subdomains |
Owner | Create a new subdomain |
PUT |
/servers/{server}/subdomains/{id} |
Owner | Update a subdomain record |
DELETE |
/servers/{server}/subdomains/{id} |
Owner | Delete a subdomain record |
POST |
/subdomains/check |
User | Check subdomain availability |
Request body for POST /servers/{server}/subdomains:
{
"subdomain": "mc",
"domain": "crashmedia.ca",
"allocation_id": 42,
"record_type": "A",
"target": "192.168.1.100",
"ssl": false,
"active": true
}
Minecraft Version Changer (Addon 2)
| Method | Endpoint | Auth | Description |
|---|---|---|---|
GET |
/version-changer/versions |
User | List all available Minecraft versions (categorized) |
GET |
/servers/{server}/version |
Owner | Get the current version for a server |
POST |
/servers/{server}/version |
Owner | Change the Minecraft version |
Request body for POST /servers/{server}/version:
{
"version": "1.21.4",
"reinstall": false
}
Plugin Installer (Addon 3) ✅ Stable
| Method | Endpoint | Auth | Description |
|---|---|---|---|
GET |
/plugins/search?query=world&marketplace=spigot |
User | Search plugins from live marketplace APIs |
GET |
/plugins/popular?marketplace=spigot |
User | Get popular plugins from live marketplace APIs |
GET |
/servers/{server}/plugins |
Owner | List installed plugins on a server |
POST |
/servers/{server}/plugins |
Owner | Install a plugin (downloads JAR via Wings API) |
DELETE |
/servers/{server}/plugins/{id} |
Owner | Uninstall a plugin (deletes JAR via Wings API) |
POST |
/servers/{server}/plugins/{id}/toggle |
Owner | Enable/disable a plugin |
Note: All marketplace queries hit live APIs (Modrinth, Hangar, SpigotMC,
CurseForge). Installation downloads the JAR and deploys it to the server's
plugins/ directory via the Wings daemon API. CurseForge requires
CURSEFORGE_API_KEY in .env; Modrinth, Hangar, and SpigotMC require no API key.
Properties Manager (Addon 4)
| Method | Endpoint | Auth | Description |
|---|---|---|---|
GET |
/servers/{server}/properties |
Owner | Get all server properties with definitions |
POST |
/servers/{server}/properties |
Owner | Update server properties |
POST |
/servers/{server}/properties/reset |
Owner | Reset all properties to Minecraft defaults |
Request body for POST /servers/{server}/properties:
{
"properties": {
"gamemode": "creative",
"difficulty": "hard",
"pvp": false,
"max-players": 50
}
}
Icon Changer (Addon 5)
| Method | Endpoint | Auth | Description |
|---|---|---|---|
GET |
/servers/{server}/icon |
Owner | Get current server icon URL and metadata |
POST |
/servers/{server}/icon |
Owner | Upload a new server icon (PNG, max 2MB) |
DELETE |
/servers/{server}/icon |
Owner | Remove the server icon |
Request for POST /servers/{server}/icon:
Multipart form upload with field name icon.
Admin Panel Endpoints (/admin/vertex)
These require Pterodactyl's admin middleware and are throttled at 30 req/min.
| Method | Endpoint | Description |
|---|---|---|
GET |
/admin/vertex |
Theme settings page |
POST |
/admin/vertex |
Update theme settings |
POST |
/admin/vertex/reset |
Reset all theme settings to defaults |
GET |
/admin/vertex/editor |
Color editor page |
GET |
/admin/vertex/announcements |
List all announcements |
POST |
/admin/vertex/announcements |
Create an announcement |
PUT |
/admin/vertex/announcements/{id} |
Update an announcement |
DELETE |
/admin/vertex/announcements/{id} |
Delete an announcement |
GET |
/admin/vertex/subdomains |
List all subdomains (global) |
GET |
/admin/vertex/subdomains/create |
Create subdomain form |
POST |
/admin/vertex/subdomains |
Store a new subdomain |
GET |
/admin/vertex/subdomains/{id}/edit |
Edit subdomain form |
PUT |
/admin/vertex/subdomains/{id} |
Update a subdomain |
DELETE |
/admin/vertex/subdomains/{id} |
Delete a subdomain |
Total: 29 client API endpoints + 1 public endpoint + 10 admin web routes = 40 total endpoints.
Architecture
Integration Model
Vertex is a Laravel package that integrates with Pterodactyl Panel through standard Laravel mechanisms. No core Pterodactyl files are modified.
┌──────────────────────────────────────────────────────────────────┐
│ Pterodactyl Panel (Laravel Application) │
│ │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ Vertex Theme Package (crashmedia/vertex-theme) │ │
│ │ │ │
│ │ composer.json → extra.providers: │ │
│ │ VertexServiceProvider (auto-discovery) │ │
│ │ │ │
│ │ ┌──────────────────────┐ ┌───────────────────────────┐ │ │
│ │ │ HTTP Layer │ │ Services │ │ │
│ │ │ │ │ │ │ │
│ │ │ Controllers (8) │ │ ThemeService │ │ │
│ │ │ Middleware (1) │ │ ColorService │ │ │
│ │ │ Traits (1) │ │ ColorPresetService │ │ │
│ │ │ Routes (2 files) │ │ VersionService │ │ │
│ │ │ Views (Blade + React) │ │ PropertiesService │ │ │
│ │ └──────────────────────┘ │ IconService │ │ │
│ │ │ PluginMarketplaceService │ │ │
│ │ ┌──────────────────────┐ └───────────────────────────┘ │ │
│ │ │ Data Layer │ │ │
│ │ │ │ ┌───────────────────────────┐ │ │
│ │ │ Models (6) │ │ Frontend │ │ │
│ │ │ Migrations (7) │ │ │ │ │
│ │ │ Redis Cache │ │ React SPA bundle │ │ │
│ │ └──────────────────────┘ │ (webpack-built, 15+ │ │ │
│ │ │ components, 3 hooks, │ │ │
│ │ │ 2 providers, 1 store) │ │ │
│ │ │ │ │ │
│ │ │ CSS (4 files) │ │ │
│ │ │ I18n (5 languages) │ │ │
│ │ └───────────────────────────┘ │ │
│ └────────────────────────────────────────────────────────────┘ │
│ │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ Pterodactyl Core (untouched) │ │
│ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │
│ │ │ Server │ │ User │ │ Node │ │ Egg │ │ │
│ │ │ Model │ │ Model │ │ Model │ │ Model │ │ │
│ │ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │ │
│ └────────────────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────────────┘
Data Flow
User visits any Pterodactyl page
│
▼
InjectThemeSettings Middleware (applied to all web routes)
│
├── Cache::remember('vertex.injected_settings', 3600)
│ └── ThemeSetting::pluck('value', 'key')
│
├── Merge with config('vertex.defaults')
│
├── ColorService::generatePalette(primary_color)
│ └── HSL interpolation → shades 50-900
│ └── Rendered as CSS custom properties on :root
│
├── View::share('vertexConfig', $config)
│ └── Available in all Blade templates via $vertexConfig
│
└── request->attributes->set('vertex_config', $config)
└── Consumed by Blade layout → passes to React root
React SPA initialization:
│
▼
index.ts → fetches GET /api/client/vertex/theme
│
▼
ThemeProvider (React Context) receives:
- config (colors, font, radius, layout, login_layout)
- palette (50-900 shades)
- features (all feature flags)
- user preferences (dark_mode, privacy_mode)
│
├── ColorEditor.tsx → preview/apply/preset endpoints
├── ThemeToggle.tsx → dark/light toggle
├── PrivacyFilter.tsx → blur sensitive data
├── ServerSorter.tsx → @dnd-kit drag-and-drop
├── FloatingWindowManager.tsx → context for windows
├── AnnouncementBanner.tsx → active announcements
└── Addon components → VersionChanger, PropertiesManager,
PluginInstaller, SubdomainManager, IconChanger
Caching Strategy
| Cache Key | TTL | Purpose | Invalidation |
|---|---|---|---|
vertex.theme_config |
3600s | Merged theme settings | Cleared on any setting update or reset |
vertex.injected_settings |
3600s | Raw settings for middleware | Same as above |
vertex.settings |
N/A | Legacy key (cleared on updates) | — |
vertex.minecraft_versions |
3600s | Mojang manifest | Expires naturally |
Cache is invalidated by ThemeService::updateSettings(), resetToDefaults(),
and model events.
Database Schema
| Migration | Table | Purpose |
|---|---|---|
2026_05_20_000001 |
theme_settings |
Key-value store for all theme configurations |
2026_05_20_000002 |
announcements |
Announcement records with scheduling |
2026_05_20_000003 |
server_sort_preferences |
Per-user server sort order |
2026_05_20_000004 |
(alters users) |
Adds vertex_dark_mode and vertex_privacy_mode columns |
2026_05_20_000010 |
subdomains |
Subdomain records linked to servers |
2026_05_20_000012 |
installed_plugins |
Plugin installation records |
2026_05_20_000013 |
server_icons |
Server icon metadata and file paths |
Installation
Prerequisites
- PHP 8.2+
- Pterodactyl Panel v1.12.2+
- Node.js 20+ (for building frontend assets)
- Composer 2+
- NPM 9+
- Redis or Memcached (recommended for caching)
Quick Install (Recommended)
cd /var/www/pterodactyl
# Require the package via Composer
composer require crashmedia/vertex-theme
# Run the installer — publishes config, runs migrations, publishes assets
php artisan vertex:install
# Build frontend assets
cd vendor/crashmedia/vertex-theme
npm ci && npm run build:all
# Return to panel root and clear cache
cd /var/www/pterodactyl
php artisan optimize:clear
Manual Installation (Development)
cd /var/www/pterodactyl
git clone https://git.crashmedia.ca/crashmedia/vertex-pterodactyl.git app/extensions/vertex-theme
Add to your panel's composer.json:
{
"repositories": [
{
"type": "path",
"url": "app/extensions/vertex-theme"
}
]
}
Then:
composer require crashmedia/vertex-theme "@dev"
php artisan vertex:install
Post-Installation
- Visit
/admin/vertexin your Pterodactyl panel to configure the theme - Set your primary color, choose a layout, and configure announcements
- The CSS and JS assets are automatically injected by the package — no manual Blade template edits are required
- Run migrations if prompted:
php artisan migrate
Configuration
Feature Flags
All feature flags live in config/vertex.php under the features key:
'features' => [
'announcements' => true, // ✅ Stable — Announcement manager
'server_sorting' => true, // ✅ Stable — Drag-and-drop sorting
'custom_css' => true, // ✅ Stable — Custom CSS injection (admin panel)
'dark_mode_toggle' => true, // ✅ Stable — Dark/light mode toggle
'custom_logo' => true, // ✅ Stable — Custom logo upload
'page_builder' => true, // 📋 Planned — Dashboard widget system
'privacy_mode' => true, // ✅ Stable — IP/sensitive data blurring
'floating_windows' => true, // ✅ Stable — Floating window multitasking
'pwa_support' => false, // 📋 Planned — Service worker not written
'turnstile' => false, // 📋 Planned — Requires Cloudflare Turnstile keys
'plugin_installer' => true, // ✅ Stable — 4 live marketplace APIs + Wings deployment
'icon_changer' => true, // ⚠️ Partial — Needs intervention/image dep
'subdomain_manager' => true, // ⚠️ Partial — DNS/SSL automation missing
'version_changer' => true, // ✅ Stable — Mojang manifest + egg variable update
],
Layout Options
'layouts' => [
'default' => 'Default Sidebar (Left)',
'sidebar-right' => 'Sidebar Right',
'topnav' => 'Top Navigation',
'compact' => 'Compact View',
'combined' => 'Combined (Sidebar + Topnav)',
],
Login Layouts (📋 Planned)
'login_layouts' => [
'default' => 'Centered Card',
'split' => 'Split Screen',
'fullscreen' => 'Full Screen Background',
'minimal' => 'Minimal',
],
Default Theme Values
'defaults' => [
'primary_color' => '#2563eb',
'background_color' => '#f8fafc',
'surface_color' => '#ffffff',
'text_color' => '#0f172a',
'font_family' => "'IBM Plex Sans', system-ui, sans-serif",
'border_radius' => '0.5rem',
'dark_mode' => false,
'layout' => 'default',
'login_layout' => 'default',
],
Environment Variables
| Variable | Default | Description |
|---|---|---|
MC_VERSION_VARIABLE |
MC_VERSION |
Egg variable name for Minecraft version changer |
CURSEFORGE_API_KEY |
— | API key for CurseForge marketplace (plugin installer) |
Development
Setup
git clone https://git.crashmedia.ca/crashmedia/vertex-pterodactyl.git
cd vertex-pterodactyl
# Install dependencies
npm ci
composer install
# Build all assets
npm run build:all
# Development mode (watch for changes)
npm run dev
Build Commands
| Command | Description |
|---|---|
npm run build |
Build the Webpack JS bundle (vertex.bundle.js) |
npm run build:css |
Build the main stylesheet (vertex.css) |
npm run build:css-dark |
Build dark mode overrides (vertex-dark.css) |
npm run build:css-admin |
Build admin panel styles (vertex-admin.css) |
npm run build:all |
Build all assets (JS + CSS + dark + admin + floating) |
npm run dev |
Watch mode for development |
Project Structure
vertex-pterodactyl/
├── config/
│ └── vertex.php # Package configuration
├── database/
│ └── migrations/ # 7 migration files
├── resources/
│ ├── css/
│ │ ├── vertex.css # Main stylesheet
│ │ ├── vertex-dark.css # Dark mode overrides
│ │ ├── vertex-admin.css # Admin panel styles
│ │ └── vertex-floating.css # Floating window styles
│ ├── lang/
│ │ ├── en/ # English (full)
│ │ ├── nl/ # Dutch (messages only)
│ │ ├── de/ # German (messages only)
│ │ ├── fr/ # French (messages only)
│ │ └── es/ # Spanish (messages only)
│ ├── scripts/vertex/
│ │ ├── index.ts # React entry point
│ │ ├── components/ # 15 React components
│ │ │ ├── AnnouncementBanner.tsx
│ │ │ ├── ColorEditor.tsx
│ │ │ ├── FloatingTaskbar.tsx
│ │ │ ├── FloatingWindow.tsx
│ │ │ ├── FloatingWindowManager.tsx
│ │ │ ├── IconChanger.tsx
│ │ │ ├── PluginInstaller.tsx
│ │ │ ├── PrivacyFilter.tsx
│ │ │ ├── PropertiesManager.tsx
│ │ │ ├── ServerContextMenu.tsx
│ │ │ ├── ServerSorter.tsx
│ │ │ ├── SortableItem.tsx
│ │ │ ├── SubdomainManager.tsx
│ │ │ ├── ThemeToggle.tsx
│ │ │ └── VersionChanger.tsx
│ │ ├── hooks/
│ │ │ ├── useLocalStorage.ts
│ │ │ ├── useServerContextMenu.ts
│ │ │ └── useServerSort.ts
│ │ ├── providers/
│ │ │ ├── AnnouncementProvider.tsx
│ │ │ └── ThemeProvider.tsx
│ │ ├── stores/
│ │ │ └── themeStore.ts
│ │ ├── layouts/
│ │ │ └── ThemeWrapper.tsx
│ │ └── types/
│ │ └── theme.ts
│ └── views/admin/ # Blade templates for admin panel
├── routes/
│ ├── api.php # 29 client API routes
│ └── web.php # 10 admin web routes
├── src/
│ ├── Console/Commands/
│ │ └── ThemeInstallCommand.php # php artisan vertex:install
│ ├── Http/
│ │ ├── Controllers/ # 8 controllers
│ │ │ ├── AnnouncementController.php
│ │ │ ├── IconController.php
│ │ │ ├── PluginController.php
│ │ │ ├── PropertiesController.php
│ │ │ ├── ServerSortController.php
│ │ │ ├── SubdomainController.php
│ │ │ ├── ThemeController.php
│ │ │ └── VersionChangerController.php
│ │ ├── Middleware/
│ │ │ └── InjectThemeSettings.php
│ │ └── Traits/
│ │ └── ChecksServerAccess.php
│ ├── Models/ # 6 Eloquent models
│ │ ├── Announcement.php
│ │ ├── InstalledPlugin.php
│ │ ├── ServerIcon.php
│ │ ├── ServerSortPreference.php
│ │ ├── Subdomain.php
│ │ └── ThemeSetting.php
│ ├── Providers/
│ │ └── VertexServiceProvider.php # Laravel auto-discovery provider
│ └── Services/ # 7 service classes
│ ├── ColorPresetService.php
│ ├── ColorService.php
│ ├── IconService.php
│ ├── PluginMarketplaceService.php
│ ├── PropertiesService.php
│ ├── ThemeService.php
│ └── VersionService.php
├── screenshots/
│ ├── 03-vertex-plugin-installer.png
│ ├── 04-vertex-color-editor.png
│ ├── 05-vertex-floating-windows.png
│ ├── 06-vertex-announcements.png
│ ├── 07-vertex-properties-manager.png
│ ├── 08-vertex-subdomain-manager.png
│ └── 09-vertex-icon-version-changer.png
├── composer.json
├── package.json
├── webpack.config.js
├── postcss.config.js
├── tailwind.config.js
├── tsconfig.json
└── .gitignore
Security
Access Control
- Server-scoped authorization — All addon endpoints use the
ChecksServerAccesstrait to verify server ownership or subuser membership before any operation. Admin users bypass these checks via$user->root_admin. - Admin routes — Protected by Pterodactyl's
auth+adminmiddleware and throttled at 30 requests/minute. - Client API — Protected by
authmiddleware and throttled at 60 requests/minute. - Public endpoint —
/api/vertex/public/themerequires no authentication and only exposes non-sensitive theme configuration.
Input Validation
- Color values — Strict hex format validation (
/^#[0-9a-fA-F]{6}$/) - Subdomains — Validated against DNS naming conventions
(
/^[a-zA-Z0-9]([a-zA-Z0-9\-]*[a-zA-Z0-9])?$/) - File uploads — Icon uploads validated by MIME type (PNG only), and size (max 2MB). Server-side resizing to exactly 64×64 pixels.
- Version strings — Validated against the Mojang version manifest before updating egg variables.
General Protections
- CSRF — All web routes protected by Laravel CSRF middleware
- XSS Prevention — Blade templates use
{{ }}escaped output; React components are JSX (auto-escaped by React) - Error Handling — Exception messages are logged server-side and replaced with generic messages in API responses
- SQL Injection — All queries use Eloquent ORM with parameter binding
- Rate Limiting — Admin routes: 30 req/min; Client routes: 60 req/min
Package Scope
Vertex does not modify any Pterodactyl core files. It uses:
- Laravel package auto-discovery (
composer.jsonextra.providers) - Middleware injection (
InjectThemeSettings) - View sharing (
View::share()) - Standard Eloquent relations on Pterodactyl models (no modifications)
Browser Support
| Browser | Minimum Version |
|---|---|
| Chrome / Edge | 90+ |
| Firefox | 90+ |
| Safari | 15+ |
| Opera | 76+ |
License
MIT License — see LICENSE for details.
Copyright (c) 2026 CrashMedia
Known Issues & Roadmap
Immediate Priorities
- Add
intervention/imagetocomposer.json— Icon uploads will fail without it. - Fix subdomain unique constraint — Should be scoped by domain, not global.
- Create theme.php translations — Dutch, German, French, and Spanish translations for UI labels.
Upcoming Features
| Feature | Target |
|---|---|
| DNS API integration (Cloudflare, Route53) for subdomain manager | v1.1 |
| SSL/ACME automation for subdomains | v1.1 |
| Login screen variants | v1.1 |
| PWA service worker and manifest | v1.2 |
| Cloudflare Turnstile integration | v1.2 |
| Page builder drag-and-drop widgets | v1.2 |
| File manager enhancements (folder upload, Monaco editor) | v1.2 |
| Font & icon picker UI | v1.2 |
| Email template customization | v1.3 |
| Settings export/import | v1.3 |
Support
- Repository: https://git.crashmedia.ca/crashmedia/vertex-pterodactyl
- Issues: Use the issue tracker in the repository
- Security: See SECURITY.md for vulnerability reporting
- Contributing: See CONTRIBUTING.md for contribution guidelines
- Changelog: See CHANGELOG.md for release history































