Chronicler #85 direct fix (Code unavailable on mobile):
- ModpackAPIController: add pending_calibration flag to serverStatus response
- wrapper.tsx: add pending_calibration to StatusData interface
- wrapper.tsx: render Identify Version button when pending_calibration is true
Replaces false green checkmark for servers needing calibration.
Installer-method servers had full filenames as current_version (e.g.
"DeceasedCraft_Beta_DH_Edition_5.10.16") which prevented reaching
pending_calibration. Now only uses DB current_version if file_id is
also set (validated) or detection method isn't installer.
Migration clears existing stale rows → pending_calibration.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
CheckModpackUpdates:
- Reads .modpack-checker.json Truth File from server filesystem
- Falls back to manifest.json, extracts fileID, writes Truth File
- NEVER seeds current_version from latest API result
- Unknown version → status: pending_calibration (not up_to_date)
- Removed seedCurrentVersion heuristic — replaced with Truth File
- writeTruthFile() helper writes .modpack-checker.json via Wings
ModpackAPIController:
- calibrate() now writes Truth File after DB update
- Persists across server reinstalls and cron runs
wrapper.tsx:
- pending_calibration: shows "Version unknown" + "Identify Version" button
- Ignored servers: muted card with "Resume" button (not hidden)
- Extracted renderCalibrateDropdown() for reuse
- Error state shows message instead of vanishing
Migration:
- Updates existing unknown+detected rows to pending_calibration
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
ModpackApiService: regex extracts semver from CurseForge displayName
"All the Mods 9-0.1.0" → version: "0.1.0", display_name: full string
Widget: short name + version helpers
"All the Mods 9 - ATM9" → "ATM9"
Display: "ATM9 0.1.0 → 1.0.0 ↑" (update) or "✓ ATM9 — 1.0.0" (current)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Cron and controller wrap modpack_installations queries in try/catch
- Falls through to egg variable / file detection if table missing
- Added migration with IF NOT EXISTS for fresh installs
- Migration won't drop the table (may be Pterodactyl-owned)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Widget redesign:
- Zero-click: loads cached status on mount via GET /status (no API calls)
- Shows platform icon + modpack name + version comparison
- Orange background + arrow (current → latest) when update available
- Cyan + checkmark when up to date
- Refresh button triggers manual check
- Calibrate button opens dropdown with last 10 releases
- Ignore button hides non-modpack servers
- Current release highlighted in calibrate dropdown
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Priority 2 — Date-time seeding:
- fetchFileHistory() for CurseForge, Modrinth, FTB
- seedCurrentVersion() matches release closest to server install date
- Falls back to latest if no history or no install date
Priority 3 — New endpoints:
- GET /servers/{server}/status — zero-click cached status
- GET /servers/{server}/releases — recalibrate dropdown (10 releases)
- POST /servers/{server}/calibrate — save user's version selection
- POST /servers/{server}/ignore — toggle is_ignored flag
Priority 5 — BCC log parsing:
- detectFromBccLog() reads logs/latest.log for BetterCompatibilityChecker
- Extracts modpack name + version from BCC output line
- Skips CHANGE_ME values
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Migration: adds current_file_id, latest_file_id, is_ignored columns
- ModpackApiService: all 4 platforms now return file_id in response
- CheckModpackUpdates: file ID comparison (preferred) with string fallback
- detectCurseForge: extracts manifest['version'] as installed_version
- Cron skips is_ignored servers
- Detection priority: manifest version > egg var > DB record > seed latest
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Detection priority in manualCheck():
1. Egg variables
2. modpack_installations table (NEW)
3. DaemonFileRepository file scan
4. Cached cron data from modpackchecker_servers (NEW)
Also returns current_version and update_available in response
so the console widget can show version comparison.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When a server is first detected, current_version is set to latest_version
(the pack was just installed = it's current). On future runs, if the API
returns a newer latest_version, the stored current_version stays and we
detect the update. Also preserves egg variable and existing DB values.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Key may have whitespace from dbGet. Added Log::debug with key length
and modpack ID to diagnose the 403.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Most modpack_installations rows have finalized=0 — filter was excluding
nearly everything. Now takes the most recent installation row regardless
of finalized status.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Detection priority now:
1. modpack_installations table (panel's own install data — fastest)
2. Egg variables (MODPACK_PLATFORM/MODPACK_ID)
3. DaemonFileRepository file scan (last resort fallback)
This immediately detects all 19 CurseForge servers on the live panel
without any Wings calls or egg variable configuration.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- CheckModpackUpdates now scans ALL servers, not just egg-configured ones
- Step 1: egg variables (fastest)
- Step 2: DaemonFileRepository file detection (manifest.json, modrinth.index.json)
- Step 3: mark unconfigured if nothing found
- Respects is_user_overridden flag for manual configs
- New migration adds detection_method + is_user_overridden columns
- Per Gemini hybrid detection consultation (2026-04-06)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
accessibleServers() only returns explicitly assigned servers — admins
don't get them on the client API side. Now checks root_admin and
returns all servers for admin users.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Node <16 fails fast with helpful error
- Node 17+ gets --openssl-legacy-provider for CSS module compat
- yarn build:production runs automatically after all injections
- Graceful fallback if yarn missing or build fails (admin + console still work)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Moved dashboard badges to "Upcoming v1.1.0" in both BBB listings
- Updated ACTIVE_CONTEXT — all code-side Phase 11 work done
- Waiting on Michael for BBB listings + live panel green light
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Dark #1a1a2e background, brand color borders and headings only,
light gray body text. Matches Pterodactyl dark theme.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Rewrote view.blade.php to use @extends('layouts.admin'), @section blocks,
AdminLTE box/callout/table classes, and standard Pterodactyl admin chrome.
Replaces custom inline styles with framework components.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- LicenseService.php: strtoupper(trim()) before sending to Arbiter
- mvc.js: toUpperCase().trim() on activate, validate, deactivate, webhook
- verifymvc.js: toUpperCase().trim() on /verify-mvc Discord command
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Blueprint generates its own controller at app/Http/Controllers/Admin/
Extensions/modpackchecker/. Our controller.php with LicenseService DI
now overwrites it during build so the route uses the licensed version.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Standard ($14.99): CurseForge, Modrinth, Technic, daily checks, 48h support
Professional ($24.99): + FTB, custom intervals, Discord webhooks, 24h support
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The admin controller was failing because Laravel's DI container had stale
class resolution cache. Added php artisan optimize:clear at end of build.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Changed cron example from '0 */6 * * *' to '0 0,6,12,18 * * *' to avoid
the */ closing the docblock prematurely and causing a syntax error.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Blueprint's requests.app merge doesn't create new subdirectories reliably.
build.sh now copies LicenseService, ValidateLicense, CheckModpackUpdates,
and ModpackAPIController directly into app/ during blueprint -install.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- New discord/verifymvc.js: verifies BBB order, links discord_id, assigns customer role
- Wired into events.js handler and index.js command registration
- Reads MVC_CUSTOMER_ROLE_ID from env — Chronicler creates role + sets value
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- New service: src/services/awakenedConcierge.js
- Fetches Discord username via Discord API
- Calls Dify Awakened Concierge app (Gemma 4) for personalized message
- Posts to #introductions (1403981218252324884) with typing indicator
- Marks welcomed_at in subscriptions table
- Non-fatal: welcome failure never breaks checkout flow
- stripe.js: calls welcomeNewMember() after syncRole() on checkout complete
- .env: CONCIERGE_API_KEY added to Command Center
Fire + Frost + Foundation 💙🔥❄️
Source code complete for all 3 versions (1.21.1, 1.20.1, 1.16.5).
1.20.1 and 1.16.5 jars built successfully. 1.21.1 NeoForge decompiler
needs >4GB RAM — this server only has ~4GB total.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Standard $14.99 / Professional $24.99 — two-tier model.
This pricing is FINAL per original marketing strategy.
Claude (Chronicler #83 - The Compiler) <claude@firefrostgaming.com>
Fixes 10 issues from Blueprint extension code review:
- CurseForge API key now reads via Blueprint dbGet() matching admin save
- PRO-tier fields (webhook, interval) enforced server-side, not just UI
- json_decode results validated before accessing parsed data
- Null user guard on getStatus() endpoint
- 429 response uses consistent error key format
- Modrinth slug derivation strips special chars, documented as fallback
- Check interval dropdown reflects saved value
- API key input changed to password type
- TypeScript error typing narrowed from any to unknown
- Removed unused DB import from ModpackApiService
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Fixes 10 issues from full code review:
- License corrected from MIT to Commercial
- Deprecated datetime.utcnow() replaced with timezone-aware alternative
- PHP array bounds checks added for all platform API responses
- Modrinth file detection now derives project slug instead of using MC version
- validate_api_key() no longer swallows network errors
- HTTP timeouts added to all external API calls in PHP
- Empty API key rejection added to CLI
- Corrupted config now warns on stderr instead of failing silently
- Error response format made consistent across controller
- Docs updated with correct repo URL and clearer CurseForge ID instructions
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
NeoForge 1.21.1, Forge 1.20.1, Forge 1.16.5
All compiled and deployed to NextCloud (Task #136)
Source committed for Task #138 (CurseForge generic fork)
CLAUDE.md with build environment docs included
Claude (Chronicler #83 - The Compiler) <claude@firefrostgaming.com>
Adds a visual asset library to the social calendar form, backed by
the firefrost-operations-manual branding/ directory with on-the-fly
thumbnail generation via sharp.
- src/routes/admin/branding-assets.js: /list scans both branding/ and
docs/branding/ recursively, groups by category directory. /thumb
generates 256px webp thumbnails from source, caches to disk keyed
by sha1(path + mtime) so edits bust the cache automatically. Path
traversal protection + scope check on thumb requests.
- src/views/admin/social-calendar/_assets.ejs: modal body with
category-grouped grid of lazy-loaded thumbnails, click-to-insert.
- Form modal gets a 'Browse assets' button next to Media Notes.
- Calendar shell gets a second modal (higher z-index) and a
sppInsertAsset() helper that appends the clicked filename to the
media_notes textarea.
Infrastructure (not in repo):
- /opt/firefrost-ops-manual clone added to Command Center
- /etc/systemd/system/firefrost-ops-sync.{service,timer} pulls
every 15 minutes to keep assets fresh
- /var/cache/arbiter/branding-thumbs created for thumb cache
- sharp added as Arbiter dependency (libvips 8.17.3)
Chronicler #81
Week-at-a-glance post planning for Meg across 8 platforms (tiktok,
facebook, instagram, x, bluesky, youtube, twitch, reddit).
- DB: extends social_platform enum with youtube/twitch/reddit and adds
new social_post_plans table with scheduled_at, platforms[], status
(draft/ready/scheduled/published/skipped), caption, hashtags[],
media_notes, link, assigned_to, notes, created_by
- src/routes/admin/social-calendar.js: shell, HTMX week view, create,
update, delete, edit/new form endpoints. Week-based navigation with
prev/next/this-week. Hashtag parsing dedupes and strips leading #
- src/views/admin/social-calendar/: index.ejs shell with modal,
_week.ejs 7-column grid with per-day quick-add, _form.ejs full CRUD
form with platform checkboxes, datetime picker, status dropdown,
both free-form caption and dedicated hashtag field
- Nav link added under Community, with corrected highlight logic so
/admin/social and /admin/social-calendar don't both light up
Separate table from social_posts (which remains the analytics table).
Plans and published posts are distinct concerns.
Chronicler #81
The 'resolved' state was a dead-end — no way to change your mind after
clicking approve/deny, and no way to re-action a needs_info appeal once
the submitter replied. Action controls now show on every row regardless
of status, and resolved rows get a Reopen button that moves the appeal
back to pending.
Backend action handler already overwrites status unconditionally, so
this is purely removing a view-layer gate plus adding /:id/reopen as
a thin wrapper over the shared actionAppeal helper.
Chronicler #81
Adds /admin/appeals to Trinity Console for reviewing and actioning
ban appeals submitted via the public cancellation-refund form.
- src/routes/admin/appeals.js: shell + HTMX list + approve/deny/info
actions, all transactional with admin_audit_log entries
- src/views/admin/appeals/index.ejs: shell with 30s polling container
- src/views/admin/appeals/_list.ejs: stats cards + actionable table
- src/routes/admin/index.js: router mount at /admin/appeals
- src/views/layout.ejs: nav link under Grace Period
Closes the last open piece of Task #126 Phase 2. Trinity can now
review appeals in-console without reading Discord + running SQL.
Chronicler #81