BREAKING CHANGES - folder structure reorganized: OLD STRUCTURE (broken): Controllers/ModpackAPIController.php console/CheckModpackUpdates.php NEW STRUCTURE (working): app/Http/Controllers/ModpackAPIController.php app/Console/Commands/CheckModpackUpdates.php CHANGES: 1. Moved controller to app/Http/Controllers/ - Namespace changed: Pterodactyl\Http\Controllers - This aligns with Laravel's PSR-4 autoloading - Blueprint's requests.app field merges into Pterodactyl's app/ 2. Moved console command to app/Console/Commands/ - Now properly registered with Laravel's command system - Run with: php artisan modpackchecker:check 3. Updated conf.yml: - Set requests.app: 'app' (enables app/ folder merging) - Cleared data.directory (was pointing to non-existent folder) - Cleared dashboard.wrapper (TSX not supported, use build.sh) 4. Updated routes/client.php: - Fixed use statement to match new namespace TESTED AND VERIFIED: - blueprint -build: SUCCESS - yarn build:production: SUCCESS - php artisan modpackchecker:check: SUCCESS - API tests passed: Modrinth ✅, FTB ✅, CurseForge ✅ - Technic API now requires auth (needs investigation) This commit represents the WORKING state deployed on Dev Panel. Signed-off-by: Claude (Chronicler #63) <claude@firefrostgaming.com>
ModpackChecker — Pterodactyl Blueprint Extension
Version: 1.0.0
Author: Firefrost Gaming
License: Proprietary (Commercial product for BuiltByBit)
A Pterodactyl Panel extension that checks modpack versions across CurseForge, Modrinth, FTB, and Technic platforms. Shows update status on the dashboard and provides manual version checks from the server console.
Table of Contents
- Features
- Architecture Overview
- File Structure
- Installation
- Configuration
- Usage
- Development
- API Reference
- Troubleshooting
- Design Decisions
Features
Dashboard Badge
- Shows a colored dot next to each server name on the dashboard
- 🟠 Orange (Fire #FF6B35): Update available
- 🟢 Teal (Frost #4ECDC4): Up to date
- Hover for version details tooltip
- Single API call per page load (cached globally)
Console Widget
- "Check for Updates" button on each server's console page
- Real-time version check against platform API
- Shows modpack name, current version, and latest version
Admin Panel
- Configure CurseForge API key
- View extension status
- (Future: Rate limit settings, notification preferences)
Supported Platforms
| Platform | ID Type | API Key Required |
|---|---|---|
| CurseForge | Numeric project ID | ✅ Yes |
| Modrinth | Project ID or slug | ❌ No |
| FTB | Numeric modpack ID | ❌ No |
| Technic | URL slug | ❌ No |
Architecture Overview
┌─────────────────────────────────────────────────────────────────────────────┐
│ MODPACK VERSION CHECKER │
│ Architecture Diagram │
└─────────────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────┐
│ CRON JOB (runs every 4-6 hrs) │
│ php artisan modpackchecker:check │
│ │
│ • Finds servers with MODPACK_* │
│ • Calls platform APIs one by one │
│ • 2-second delay between calls │
│ • Stores results in database │
└──────────────────┬──────────────────┘
│
▼
┌─────────────────────────────────────┐
│ DATABASE CACHE │
│ modpackchecker_servers table │
│ │
│ • server_id, server_uuid │
│ • platform, modpack_id │
│ • current_version, latest_version │
│ • update_available (boolean) │
│ • last_checked timestamp │
└──────────────────┬──────────────────┘
│
┌──────────────────┴──────────────────┐
│ │
▼ ▼
┌───────────────────────────┐ ┌───────────────────────────┐
│ DASHBOARD BADGE │ │ CONSOLE WIDGET │
│ (UpdateBadge.tsx) │ │ (wrapper.tsx) │
│ │ │ │
│ • Reads from cache ONLY │ │ • Manual "Check" button │
│ • Never calls external │ │ • LIVE API call │
│ • One API call per page │ │ • Single server only │
│ • Shows 🟠 or 🟢 dot │ │ • Shows full details │
└───────────────────────────┘ └───────────────────────────┘
Why This Architecture?
The Problem: A panel with 50 servers and 20 active users could generate thousands of API calls per day if each dashboard view triggered live checks.
The Solution:
- Cron job handles external API calls with rate limiting
- Dashboard reads from local cache only
- Console provides on-demand checks for specific servers
This was validated by Gemini AI during architectural review.
File Structure
blueprint-extension/
├── README.md # This file
├── conf.yml # Blueprint configuration
├── build.sh # Injection script (runs during blueprint -build)
│
├── Controllers/
│ └── ModpackAPIController.php # API endpoints (manualCheck, getStatus)
│
├── admin/
│ ├── controller.php # Admin panel logic
│ └── view.blade.php # Admin panel UI
│
├── console/
│ └── CheckModpackUpdates.php # Laravel cron command
│
├── database/
│ └── migrations/
│ └── 2024_XX_XX_create_modpackchecker_servers_table.php
│
├── routes/
│ └── client.php # API route definitions
│
└── views/
├── server/
│ └── wrapper.tsx # Console "Check for Updates" widget
└── dashboard/
└── UpdateBadge.tsx # Dashboard status dot component
Installation
Prerequisites
- Pterodactyl Panel v1.11+
- Blueprint Framework (beta-2026-01 or newer)
- PHP 8.1+
- Node.js 18+
Steps
-
Copy extension to Blueprint directory:
cp -r blueprint-extension /var/www/pterodactyl/.blueprint/extensions/modpackchecker chown -R www-data:www-data /var/www/pterodactyl/.blueprint/extensions/modpackchecker -
Build the extension:
cd /var/www/pterodactyl blueprint -build -
Compile frontend assets:
export NODE_OPTIONS=--openssl-legacy-provider yarn build:production -
Run database migration:
php artisan migrate -
Restart PHP-FPM:
systemctl restart php8.3-fpm -
Set up cron job:
# Add to /etc/crontab or crontab -e 0 */6 * * * www-data cd /var/www/pterodactyl && php artisan modpackchecker:check >> /dev/null 2>&1
Configuration
Server Egg Variables
For modpack detection, set these variables in your server's egg:
| Variable | Description | Example |
|---|---|---|
MODPACK_PLATFORM |
Platform name | modrinth, curseforge, ftb, technic |
MODPACK_ID |
Platform-specific ID | adrenaserver (Modrinth slug) |
MODPACK_CURRENT_VERSION |
Installed version | 1.7.0 |
CurseForge API Key
CurseForge requires an API key. To configure:
- Apply for API access at https://docs.curseforge.com/
- Go to Admin Panel → Extensions → ModpackChecker
- Enter your API key and save
Usage
Dashboard Badge
No action needed — badges appear automatically for servers that:
- Have
MODPACK_PLATFORMegg variable set - Have been checked by the cron job at least once
Manual Check
- Go to any server's console page
- Click "Check for Updates" button
- View results showing modpack name and version status
Cron Command
Run manually for testing:
cd /var/www/pterodactyl
php artisan modpackchecker:check
Output:
Starting modpack update check...
Found 12 servers with modpack configuration
Checking: ATM9 Server (a1b2c3d4-...)
🟠 UPDATE AVAILABLE: All The Mods 9 - 0.2.60
Checking: Vanilla+ (e5f6g7h8-...)
🟢 Up to date: Vanilla+ - 1.2.0
Modpack update check complete!
Development
Local Development
- Enable Blueprint developer mode in admin panel
- Make changes in
.blueprint/dev/or.blueprint/extensions/modpackchecker/ - Run
blueprint -buildafter changes - Run
yarn build:productionfor frontend changes
Testing API Endpoints
# Manual check (requires auth token)
curl -X POST "https://panel.example.com/api/client/servers/{uuid}/ext/modpackchecker/check" \
-H "Authorization: Bearer {token}"
# Get all statuses (requires auth token)
curl "https://panel.example.com/api/client/extensions/modpackchecker/status" \
-H "Authorization: Bearer {token}"
Adding a New Platform
- Add check method to
ModpackAPIController.php(e.g.,checkNewPlatform()) - Add to the
match()statement incheckVersion() - Add same method to
CheckModpackUpdates.php - Update this README
API Reference
POST /api/client/servers/{server}/ext/modpackchecker/check
Manual version check for a specific server. Makes live API call.
Response:
{
"success": true,
"platform": "modrinth",
"modpack_id": "adrenaserver",
"modpack_name": "Adrenaserver",
"latest_version": "1.7.0+1.21.1.fabric",
"status": "checked"
}
GET /api/client/extensions/modpackchecker/status
Get cached status for all servers accessible to the authenticated user.
Response:
{
"a1b2c3d4-...": {
"update_available": true,
"modpack_name": "All The Mods 9",
"current_version": "0.2.51",
"latest_version": "0.2.60"
},
"e5f6g7h8-...": {
"update_available": false,
"modpack_name": "Adrenaserver",
"current_version": "1.7.0",
"latest_version": "1.7.0"
}
}
Troubleshooting
Badge not showing
- Check server has
MODPACK_PLATFORMvariable set - Run cron command manually:
php artisan modpackchecker:check - Check
modpackchecker_serverstable for entries
"CurseForge API key not configured"
- Go to Admin → Extensions → ModpackChecker
- Enter your CurseForge API key
- Key must have mod read permissions
500 errors on check
- Check PHP error log:
tail -f /var/log/php8.3-fpm.log - Verify controller namespace:
Pterodactyl\BlueprintFramework\Extensions\modpackchecker\Controllers - Restart PHP-FPM:
systemctl restart php8.3-fpm
Build.sh not running
- Ensure file is executable:
chmod +x build.sh - Check Blueprint version supports build scripts
- Run manually from panel root:
bash .blueprint/extensions/modpackchecker/build.sh
Design Decisions
Why cache instead of live checks?
Rate limits. CurseForge allows ~1000 requests/day for personal keys. A busy panel could exhaust that in hours without caching.
Why 2-second sleep in cron?
Prevents burst traffic to APIs. 50 servers × 2 seconds = ~2 minute runtime, which is acceptable for a background job.
Why inline styles in React?
The component is injected into Pterodactyl's build. Adding CSS classes would require modifying their build pipeline. Inline styles are self-contained.
Why separate console widget and dashboard badge?
Different use cases:
- Dashboard: Quick overview, needs to be fast → cached
- Console: User wants current info → live API call is acceptable
Credits
Development Team:
- Architecture design: Gemini AI
- Implementation: Chroniclers #52, #62, #63 (Claude instances)
- Project Lead: Michael "Frostystyle" Krause
Part of Firefrost Gaming
Fire + Frost + Foundation = Where Love Builds Legacy 🔥❄️💙