- New sidebar entry for Discord
- Full server structure visualization
- Channel tree with expandable categories
- Role hierarchy with color badges
- Health checks (orphan channels, empty roles, bot roles)
- Search/filter across channels and roles
- Click channel to see permission overwrites
- Click role to see explicit channel access
- Responsive design with modal details view
Chronicler: #70
New endpoints for Trinity Console:
- GET /admin/discord/audit — Full server audit (channels, roles, structure)
- GET /admin/discord/channels — Just channels
- GET /admin/discord/roles — Just roles
Returns:
- Server info (name, member count, features)
- Categories with nested children
- Orphan channels (not in categories)
- Role hierarchy with positions and member counts
- Permission overwrites per channel
Uses existing Discord.js client from app.locals.
Chronicler #70
When hourly sync encounters servers that fail (e.g., mid-restart):
- Logs the failure count
- Schedules automatic retry in 10 minutes
- Retry only targets previously failed servers
- Clears error state on successful retry
Fixes issue where servers in daily restart would stay in error state
until manual intervention.
Chronicler #69
- Queries server_sync_log for most recent successful sync
- Displays date (e.g., 'Apr 8') and time (e.g., '3:28 AM')
- Shows yellow dash with 'Never' if no syncs recorded
Chronicler #69
Added missing dependencies that were installed on server but not in repo:
- axios: ^1.14.0
- connect-pg-simple: ^10.0.0
- date-fns: ^4.1.0
This caused deploy script to fail with MODULE_NOT_FOUND errors.
Chronicler #69
The subscriptions table uses:
- tier_level (integer) not tier_id
- mrr_value (pre-calculated) not joined to subscription_tiers
- is_lifetime (boolean) not status='lifetime'
Chronicler #69
Dashboard was showing hardcoded values:
- Servers Online: 12 (should be 22)
- Active Subscribers: 0
- Total MRR: $0
Now fetches live data:
- Server count from Pterodactyl API via getMinecraftServers()
- Subscriber count and MRR from arbiter_db subscriptions table
Files changed:
- src/routes/admin/index.js: async dashboard route with data fetching
- src/views/admin/dashboard.ejs: EJS variables instead of hardcoded values
Chronicler #69
- Retrieved from Cloudflare dashboard via MCP connector
- Was 'dashboard only, not in any git repo' - gap now closed
- Original creation: April 3, 2026 by Chronicler #56 (The Velocity)
- Proxies Pterodactyl API for live server status on website
Chronicler #68
Disabled PRO fields don't submit values, causing validation error.
Now accepts null and defaults to 'daily' in update method.
Signed-off-by: Claude (Chronicler #63) <claude@firefrostgaming.com>
conf.yml: Changed website from firefrostgaming.com to firefrostgaming.com/discord
- Discord is the primary support channel
- Link icon in Blueprint admin header now goes directly to support
Signed-off-by: Claude (Chronicler #63) <claude@firefrostgaming.com>
build.sh:
- Changed injection from ServerConsoleContainer to AfterInformation.tsx
- Card now appears in right column after Network stats
wrapper.tsx:
- Redesigned to match Pterodactyl StatBlock aesthetic
- Uses Tailwind classes (bg-gray-600, rounded, etc.)
- FontAwesome cube icon with status colors
- Compact layout: title + Check button on one line
- Fire (#FF6B35/orange-400) for updates, Frost (#4ECDC4/cyan-400) for current
Fixes layout issue identified in Wizard review.
Signed-off-by: Claude (Chronicler #63) <claude@firefrostgaming.com>
Admin Panel (view.blade.php):
- Changed CurseForge API key from password dots to plain text
- Fixed callouts: dark theme (#1a1a2e) with Frost/Fire accent borders
- Improved code tag styling for readability
- Changed support link to firefrostgaming.com/discord
README.md:
- Added Prerequisites section (PHP 8.1+, Node.js 18+, Yarn, Blueprint)
Reviewed by: Michael 'Frostystyle' Krause (The Wizard)
Signed-off-by: Claude (Chronicler #63) <claude@firefrostgaming.com>
BATCH 3 - Frontend & UI:
wrapper.tsx (Console Widget):
- FIXED: API URL from .../ext/modpackchecker/check to .../check
- Added 429 rate limit handling with user-friendly message
UpdateBadge.tsx (Dashboard Badge):
- Added 60-second TTL to global cache (was infinite)
- Prevents stale data during client-side navigation
admin/view.blade.php:
- Disabled Discord webhook field (PRO TIER badge)
- Disabled Check Interval field (PRO TIER badge)
- Added support callout linking to Discord
BATCH 4 - Documentation:
README.md:
- Fixed architecture diagram (server_uuid, status string)
- Added app/Services/ModpackApiService.php to file structure
- Fixed API endpoint URLs throughout
- Updated installation for BuiltByBit (.blueprint package)
- Updated 'Adding New Platform' instructions for Service pattern
- Added Support section with Discord link
- Changed license to explicit commercial terms
NEW: CHANGELOG.md
- Version history for future updates
- Documents v1.0.0 features
Reviewed by: Gemini AI (Architecture Consultant)
Signed-off-by: Claude (Chronicler #63) <claude@firefrostgaming.com>
NEW: app/Services/ModpackApiService.php
- Centralized API logic for all 4 platforms
- Technic build number cached for 12 hours (RV-Ready)
- Single source of truth for API calls
Controller (ModpackAPIController.php):
- Now uses injected ModpackApiService instead of duplicated code
- Added RateLimiter: 2 requests/minute per server on manualCheck()
- Returns 429 with countdown when rate limited
- Removed 400+ lines of duplicated API code
Console Command (CheckModpackUpdates.php):
- FIXED: updateDatabase() now uses server_uuid (not server_id)
- FIXED: status column uses strings ('update_available', 'up_to_date', 'error')
- FIXED: Technic API now uses dynamic build via service
- Now uses injected ModpackApiService
SECURITY:
- Rate limiting prevents API key abuse via button spam
- Technic build caching reduces external API calls
Reviewed by: Gemini AI (Architecture Consultant)
Signed-off-by: Claude (Chronicler #63) <claude@firefrostgaming.com>
BUG: Was using server_id (column doesn't exist) instead of server_uuid
BUG: Was using update_available (column doesn't exist) instead of status
FIXED:
- Changed whereIn('server_id', $serverIds) to whereIn('server_uuid', $serverUuids)
- Changed pluck('id') to pluck('uuid')
- Changed (bool) $status->update_available to $status->status === 'update_available'
This fix makes the dashboard badge API actually work!
Signed-off-by: Claude (Chronicler #63) <claude@firefrostgaming.com>
- Isometric cube with checkmark (version check concept)
- Frost (#4ECDC4) edge on left, Fire (#FF6B35) edge on right
- Subtle Firefrost branding that fits Pterodactyl's UI
- 128x128 PNG with transparency
Designed by Gemini AI, April 2026.
Signed-off-by: Claude (Chronicler #63) <claude@firefrostgaming.com>
ROOT CAUSE (Gemini consultation):
Technic blocks requests with old/deprecated build numbers. The hardcoded
'?build=1' was being rejected as an ancient launcher version.
SOLUTION:
- Fetch current stable launcher build from /launcher/version/stable4
- Use that build number in the modpack request
- Fallback to 999 if version check fails
This 'RV-Ready' approach requires zero maintenance as Technic updates
their launcher versions over time.
ALL 4 PLATFORMS NOW WORKING:
✅ Modrinth
✅ FTB
✅ CurseForge
✅ Technic
Signed-off-by: Claude (Chronicler #63) <claude@firefrostgaming.com>
- Updated file structure to show app/Http/Controllers and app/Console/Commands
- Added explanation of why the app/ folder structure is used
- Updated platform support table with working status
- Added note about Technic API 401 error (investigation needed)
Signed-off-by: Claude (Chronicler #63) <claude@firefrostgaming.com>
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>
Added professional-grade documentation throughout the codebase so any
developer can pick up this project and understand it immediately.
PHILOSOPHY:
'Hand someone the repo and say: here's what we built, here's WHY we built
it this way, here's where it's going. Make it better.' — Michael
NEW FILES:
- blueprint-extension/README.md
- Complete developer onboarding guide (400+ lines)
- Architecture diagram showing cron → cache → badge flow
- Installation steps, configuration, usage
- API reference with example responses
- Troubleshooting guide
- Design decisions with rationale
ENHANCED DOCUMENTATION:
ModpackAPIController.php:
- 60-line file header explaining purpose, architecture, critical decisions
- Detailed docblocks on every method
- Explains WHY dashboard reads cache-only (rate limits)
- Documents all four platform APIs with links
- Example request/response for each endpoint
CheckModpackUpdates.php:
- 50-line file header with usage examples
- Recommended cron schedule
- Example console output
- Documents rate limiting strategy
- Explains relationship to dashboard badges
UpdateBadge.tsx:
- 50-line file header explaining the 'dumb badge' architecture
- Detailed comments on global cache pattern
- Documents the fetch-once deduplication strategy
- Explains render conditions and why each exists
- Brand color documentation (Fire/Frost)
- Accessibility notes (aria-label)
WHAT A NEW DEVELOPER NOW KNOWS:
1. The 'why' behind every architectural decision
2. How the cron → cache → badge flow prevents rate limits
3. Which methods call external APIs vs read cache
4. How to add a new platform
5. How to troubleshoot common issues
6. The relationship between all components
This codebase is now ready to hand to a contractor with the words:
'This was made great. Make it awesome.'
Signed-off-by: Claude (Chronicler #63) <claude@firefrostgaming.com>
Phase 5 Components (completing Pyrrhus's work):
NEW FILES:
- views/dashboard/UpdateBadge.tsx: Dashboard badge component
- Shows 🟢 (up to date) or 🟠 (update available) next to server names
- Global cache prevents multiple API calls on page load
- Reads from local database, never calls external APIs directly
- Fire (#FF6B35) and Frost (#4ECDC4) brand colors
- console/CheckModpackUpdates.php: Laravel cron command
- Run with: php artisan modpackchecker:check
- Loops through servers with MODPACK_PLATFORM variable
- Checks CurseForge, Modrinth, FTB, Technic APIs
- Rate limited (2s sleep between checks)
- Stores results in modpackchecker_servers table
UPDATED FILES:
- Controllers/ModpackAPIController.php:
- Added getStatus() method for dashboard badge endpoint
- Returns all user's servers' update status in single query
- Added DB facade import
- routes/client.php:
- Added GET /extensions/modpackchecker/status route
- build.sh:
- Complete rewrite for Phase 5
- Handles both console widget AND dashboard badge
- Auto-detects extension directory (dev vs extensions)
- Copies CheckModpackUpdates.php to app/Console/Commands/
- Injects UpdateBadge into ServerRow.tsx
- Clear status output and next-steps guide
Architecture (Gemini-approved):
CRON (hourly) → Database cache → Single API endpoint → React badge
Dashboard badge is 'dumb' - only reads from cache, never external APIs
Completing work started by Chronicler #62 (Pyrrhus).
UpdateBadge.tsx was lost in Blueprint corruption - reconstructed from
handoff notes and architecture documentation.
Signed-off-by: Claude (Chronicler #63) <claude@firefrostgaming.com>
PHASE 3 COMPLETE - All systems operational on Dev Panel
Changes:
- Renamed controllers/ to Controllers/ (PSR-4 case sensitivity fix)
- Updated namespace to use capital C in Controllers
- Fixed getEggVariable() method to use correct Pterodactyl model structure
- Changed from whereHas('variable'...) to direct where('env_variable'...)
- Changed return from variable_value to server_value
- Updated routes/client.php with correct namespace
- Updated wrapper.tsx with correct API path (/api/client/extensions/...)
- Added build.sh for React component injection via sed
Tested and verified:
- Admin UI renders correctly
- Client panel loads without 500 error
- React component appears on server console page
- API call executes successfully
- Returns proper 'no modpack detected' message for unconfigured servers
Key learnings documented:
- Blueprint wrapper field is for Blade only, not TSX
- TSX components require build.sh + sed injection + yarn build
- PHP-FPM OPCache requires restart after adding new classes
- Controller namespace must match directory case exactly
Dev Panel: http://64.50.188.14:128
Test Server UUID: c0a133db-6cb7-497d-a2ed-22ae66eb0de8
Next: Phase 4 - Real modpack testing with CurseForge API
Signed-off-by: Claude (Chronicler #62) <claude@firefrostgaming.com>
WHAT THIS ADDS:
- Discord role sync on new subscriptions (checkout.session.completed)
- Discord role removal on chargebacks (charge.dispute.created)
- Grace period expiration job (hourly cron check)
- Automatic downgrade to Awakened when grace period expires
NEW FILES:
- src/services/discordRoleSync.js - Role add/remove/sync functions
- src/sync/graceExpiration.js - Grace period expiration processor
MODIFIED FILES:
- src/routes/stripe.js - Added role sync calls to webhook handlers
- src/discord/events.js - Initialize role sync service on bot ready
- src/sync/cron.js - Added grace period check to hourly job
- src/index.js - Import discordRoleSync service
PHILOSOPHY:
'We Don't Kick People Out' - expired grace periods downgrade to
permanent Awakened tier (tier 1, lifetime). Users keep community
access, just lose premium perks.
ROLE MAPPING (tier_level -> role key):
1=the-awakened, 2=fire-elemental, 3=frost-elemental,
4=fire-knight, 5=frost-knight, 6=fire-master, 7=frost-master,
8=fire-legend, 9=frost-legend, 10=the-sovereign
CHARGEBACKS:
- Immediate role removal
- Added to banned_users table
- Full audit logging
Signed-off-by: Claude (Chronicler #62) <claude@firefrostgaming.com>
Replaces MemoryStore with connect-pg-simple.
Sessions now persist across Arbiter restarts.
Table 'session' auto-created if missing.
Signed-off-by: Claude (Chronicler #61) <claude@firefrostgaming.com>
Removed power task filter — Pterodactyl doesn't include task
relationships by default. Now catches any schedule not prefixed
with [Trinity].
Signed-off-by: Claude (Chronicler #61) <claude@firefrostgaming.com>
express-ejs-layouts doesn't support nested includes.
Changed scheduler.ejs to inline the table HTML.
Changed routes to return raw HTML for HTMX partials instead of rendering.
Signed-off-by: Claude (Chronicler #61) <claude@firefrostgaming.com>
WHAT WAS DONE:
- Added POST /admin/servers/sync-all/:node endpoint
- Accepts 'tx1' or 'nc1' as node parameter
- Syncs whitelist to all servers on that node
- Returns count of synced/errors
- Wired up buttons in index.ejs with htmx
- hx-post to the new endpoint
- Results display in #sync-result span
Files changed:
- services/arbiter-3.0/src/routes/admin/servers.js (+45 lines)
- services/arbiter-3.0/src/views/admin/servers/index.ejs
Signed-off-by: Claude (Chronicler #60) <claude@firefrostgaming.com>
WHAT WAS DONE:
- Re-added MINECRAFT_NEST_IDS filtering
- Keeps the node ID mapping fix (2→NC1, 3→TX1)
WHY:
Non-Minecraft servers were appearing in the matrix.
We need to filter to only show Minecraft servers.
Signed-off-by: Claude (Chronicler #60) <claude@firefrostgaming.com>
WHAT WAS DONE:
- Removed MINECRAFT_NEST_IDS filtering
- Now shows ALL servers from Pterodactyl, not just Minecraft nests
WHY:
Trinity Console should show all servers for management,
not just those in specific nests.
Signed-off-by: Claude (Chronicler #60) <claude@firefrostgaming.com>