diff --git a/docs/code-bridge/archive/RES-2026-04-12-phase11bc-deploy.md b/docs/code-bridge/archive/RES-2026-04-12-phase11bc-deploy.md new file mode 100644 index 0000000..6a44d9f --- /dev/null +++ b/docs/code-bridge/archive/RES-2026-04-12-phase11bc-deploy.md @@ -0,0 +1,35 @@ +# Architectural Response + +**Re:** Phase 11B/C Deployment Complete +**Date:** 2026-04-12 +**From:** Chronicler #84 — The Meridian + +--- + +## Status: DEPLOYED ✅ + +### Discord Role Created +- Name: **ModpackChecker Customer** +- Color: Frost teal (#1A9ECF) +- ID: `1493061127423262870` + +### Env Var Added +``` +MVC_CUSTOMER_ROLE_ID=1493061127423262870 +``` + +### Code Deployed +- `src/discord/verifymvc.js` ✅ +- `src/discord/events.js` ✅ +- `src/index.js` ✅ + +### Verification +- Health: `{"status":"online","bot":"The Arbiter#6636"}` ✅ +- Logs: `✅ Successfully reloaded application (/) commands` ✅ +- `/verify-mvc` is live in Discord + +--- + +Proceed to Phase 11D. + +*— Chronicler #84, The Meridian* diff --git a/docs/code-bridge/status/ACTIVE_CONTEXT.md b/docs/code-bridge/status/ACTIVE_CONTEXT.md index 183957b..a9f6e11 100644 --- a/docs/code-bridge/status/ACTIVE_CONTEXT.md +++ b/docs/code-bridge/status/ACTIVE_CONTEXT.md @@ -1,19 +1,19 @@ # Code Status Update -**Last Updated:** 2026-04-12 20:45 CDT +**Last Updated:** 2026-04-12 21:15 CDT ## Current Focus -Phase 11B/C complete — /verify-mvc slash command written. Ready for Chronicler deployment + role creation. +Phase 11D complete — Blueprint extension licensing integration written. Ready for testing. ## Recently Completed -- Phase 11A: DEPLOYED ✅ — migration ran, API routes live, health check passed -- Phase 11B/C: Created `src/discord/verifymvc.js` — /verify-mvc slash command -- Phase 11B/C: Wired into events.js handler + index.js command registration -- Command assigns MVC_CUSTOMER_ROLE_ID role on successful verification +- Phase 11A: DEPLOYED ✅ — DB + API routes live on Command Center +- Phase 11B/C: DEPLOYED ✅ — /verify-mvc live, customer role created (1493061127423262870) +- Phase 11D: Created LicenseService.php — activate, validate, deactivate, grace period, tier check +- Phase 11D: Created ValidateLicense.php — `mvc:validate` Artisan command (daily phone-home) +- Phase 11D: Updated admin controller.php — license activation/deactivation in update handler +- Phase 11D: Updated admin view.blade.php — order ID input, status indicator, grace/expired banners, dynamic tier gating ## Next Steps Pending -- **DEPLOY: Chronicler restarts Arbiter + creates ModpackChecker Customer role on Discord** -- **Chronicler needs to:** set MVC_CUSTOMER_ROLE_ID in .env after creating role -- Phase 11D: Blueprint extension — license activation UI, phone-home cron, tier gating +- Phase 11D testing: build .blueprint, install, verify license flow end-to-end - Phase 11E: GitBook knowledge base migration - Phase 11F: BuiltByBit listing creation (Standard $14.99, Professional $24.99) - Phase 11G: Business hours & support boundaries diff --git a/services/modpack-version-checker/blueprint-extension/admin/controller.php b/services/modpack-version-checker/blueprint-extension/admin/controller.php index 8cac306..bce24eb 100644 --- a/services/modpack-version-checker/blueprint-extension/admin/controller.php +++ b/services/modpack-version-checker/blueprint-extension/admin/controller.php @@ -7,6 +7,7 @@ use Illuminate\View\Factory as ViewFactory; use Pterodactyl\Http\Controllers\Controller; use Pterodactyl\BlueprintFramework\Libraries\ExtensionLibrary\Admin\BlueprintAdminLibrary as BlueprintExtensionLibrary; use Pterodactyl\Http\Requests\Admin\AdminFormRequest; +use Pterodactyl\Services\LicenseService; use Illuminate\Http\RedirectResponse; class modpackcheckerExtensionController extends Controller @@ -14,54 +15,70 @@ class modpackcheckerExtensionController extends Controller public function __construct( private ViewFactory $view, private BlueprintExtensionLibrary $blueprint, + private LicenseService $licenseService, ) {} public function index(): View { - // Get current settings $curseforge_api_key = $this->blueprint->dbGet('modpackchecker', 'curseforge_api_key'); $discord_webhook_url = $this->blueprint->dbGet('modpackchecker', 'discord_webhook_url'); $check_interval = $this->blueprint->dbGet('modpackchecker', 'check_interval'); - $tier = $this->blueprint->dbGet('modpackchecker', 'tier'); - // Set defaults if empty if ($check_interval == '') { $this->blueprint->dbSet('modpackchecker', 'check_interval', 'daily'); $check_interval = 'daily'; } - if ($tier == '') { - $this->blueprint->dbSet('modpackchecker', 'tier', 'standard'); - $tier = 'standard'; - } + + $license = $this->licenseService->getState(); return $this->view->make( 'admin.extensions.modpackchecker.index', [ 'curseforge_api_key' => $curseforge_api_key, 'discord_webhook_url' => $discord_webhook_url, 'check_interval' => $check_interval, - 'tier' => $tier, + 'license' => $license, 'root' => '/admin/extensions/modpackchecker', 'blueprint' => $this->blueprint, ] ); } - /** - * @throws \Pterodactyl\Exceptions\Model\DataValidationException - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException - */ public function update(modpackcheckerSettingsFormRequest $request): RedirectResponse { + // Handle license activation + $orderId = $request->input('order_id'); + if (!empty($orderId)) { + $currentOrderId = $this->blueprint->dbGet('modpackchecker', 'order_id'); + if ($orderId !== $currentOrderId) { + $result = $this->licenseService->activate($orderId); + if (!$result['success']) { + return redirect() + ->route('admin.extensions.modpackchecker.index') + ->with('error', 'License activation failed: ' . ($result['error'] ?? 'Unknown error')); + } + } + } + + // Handle license deactivation + if ($request->input('deactivate_license') === '1') { + $this->licenseService->deactivate(); + return redirect() + ->route('admin.extensions.modpackchecker.index') + ->with('success', 'License deactivated.'); + } + + // Save standard settings $this->blueprint->dbSet('modpackchecker', 'curseforge_api_key', $request->input('curseforge_api_key') ?? ''); - // Only save PRO-tier fields if the user is on the pro tier - $tier = $this->blueprint->dbGet('modpackchecker', 'tier') ?: 'standard'; - if ($tier === 'pro') { + // Only save PRO-tier fields if licensed as professional + if ($this->licenseService->isProFeatureAllowed()) { $this->blueprint->dbSet('modpackchecker', 'discord_webhook_url', $request->input('discord_webhook_url') ?? ''); $this->blueprint->dbSet('modpackchecker', 'check_interval', $request->input('check_interval') ?? 'daily'); } - return redirect()->route('admin.extensions.modpackchecker.index')->with('success', 'Settings saved successfully.'); + return redirect() + ->route('admin.extensions.modpackchecker.index') + ->with('success', 'Settings saved successfully.'); } } @@ -70,6 +87,8 @@ class modpackcheckerSettingsFormRequest extends AdminFormRequest public function rules(): array { return [ + 'order_id' => 'nullable|string|max:64', + 'deactivate_license' => 'nullable|string', 'curseforge_api_key' => 'nullable|string|max:500', 'discord_webhook_url' => 'nullable|url|max:500', 'check_interval' => 'nullable|in:daily,12h,6h', @@ -79,6 +98,7 @@ class modpackcheckerSettingsFormRequest extends AdminFormRequest public function attributes(): array { return [ + 'order_id' => 'Order ID', 'curseforge_api_key' => 'CurseForge API Key', 'discord_webhook_url' => 'Discord Webhook URL', 'check_interval' => 'Check Interval', diff --git a/services/modpack-version-checker/blueprint-extension/admin/view.blade.php b/services/modpack-version-checker/blueprint-extension/admin/view.blade.php index 99e36f8..8471aaa 100644 --- a/services/modpack-version-checker/blueprint-extension/admin/view.blade.php +++ b/services/modpack-version-checker/blueprint-extension/admin/view.blade.php @@ -61,7 +61,81 @@ + + @if($license['status'] === 'expired') +
Your license could not be validated. Pro features are disabled. Please check your order ID or contact support.
+Could not reach license server. Grace period expires: {{ $license['grace_expires'] }}
++ Enter the order ID from your BuiltByBit purchase to activate. +
++ Tier: {{ ucfirst($license['tier']) }} + | + Last Validated: {{ $license['last_validated'] ?: 'Never' }} +
+- Get your free API key from + Get your free API key from console.curseforge.com. Required for CurseForge modpack detection.
@@ -91,7 +165,9 @@- Upgrade to Professional to receive automated update alerts in your Discord server. + @if($license['tier'] === 'professional' && $license['status'] !== 'expired') + Professional tier — Discord webhook alerts enabled. + @else + Upgrade to Professional to receive automated update alerts in your Discord server. + @endif
A new version ({{ $license['latest_version'] }}) is available.
+ + Download from BuiltByBit + +
ModpackChecker automatically detects modpacks via Egg Variables or file fingerprinting.
- Set MODPACK_PLATFORM and
+ Set MODPACK_PLATFORM and
MODPACK_ID in your server's startup variables
- for the most reliable detection, or let the extension scan for
- manifest.json /
+ for the most reliable detection, or let the extension scan for
+ manifest.json /
modrinth.index.json files.