Connect License class to the real auroraplugins.com license server

- License.php now calls the aurora-licensing plugin's REST API
  (/wp-json/aurora-licensing/v1/validate|activate|deactivate) instead of
  treating any non-empty license_key as active. Result is cached in the
  aurora_admin_license_status option for 12 hours; is_active() and
  is_feature_available() read the cache instead of hitting the network on
  every admin page load.
- Hooks update_option_aurora_admin_settings to activate/deactivate
  immediately when the license_key setting actually changes, rather than
  waiting out the cache. Instantiated in AuroraAdmin.php so that hook
  registers.
- Settings page now surfaces the real status (active/invalid/unreachable,
  with the server's own message) as a status line under the License key
  field, instead of a field that silently does nothing.
- Verified live against the real server: a fake test key correctly came
  back locked with the server's own "License key not found." message.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
This commit is contained in:
nixpanel
2026-07-03 20:26:00 -05:00
co-authored by Claude Sonnet 5
parent eb03d3dd6c
commit 540083e6e4
6 changed files with 264 additions and 30 deletions
@@ -11,9 +11,24 @@ const props = defineProps({
modelValue: { default: undefined },
// Options for multiselect fields (resolved by the page from optionsSource).
options: { type: Array, default: () => [] },
// License::status()'s cached result, only passed for the license_key field.
licenseStatus: { type: Object, default: null },
});
const emit = defineEmits(['update:modelValue']);
const licenseStatusText = computed(() => {
const s = props.licenseStatus;
if (!s || s.status === 'none') return null;
if (s.status === 'active') {
const plan = s.plan ? ` (${s.plan} plan)` : '';
return { tone: 'ok', text: `License active${plan}${s.expires_at ? ` — renews/expires ${s.expires_at}` : ''}` };
}
if (s.status === 'unreachable') {
return { tone: 'warn', text: s.message || 'Could not reach the license server — will retry automatically.' };
}
return { tone: 'error', text: s.message || 'License invalid.' };
});
const val = computed({
get: () => props.modelValue,
set: (v) => emit('update:modelValue', v),
@@ -95,6 +110,9 @@ const openFontList = () => {
<div class="field__info">
<div class="field__label">{{ field.label }}</div>
<p v-if="field.description" class="field__desc">{{ field.description }}</p>
<p v-if="licenseStatusText" class="field__license-status" :class="`field__license-status--${licenseStatusText.tone}`">
{{ licenseStatusText.text }}
</p>
</div>
<div class="field__control">
@@ -280,6 +298,10 @@ const openFontList = () => {
}
.field__label { font-size: 0.95rem; font-weight: 600; color: var(--aurora-text); }
.field__desc { margin: 6px 0 0; font-size: 0.8125rem; color: var(--aurora-text-muted); line-height: 1.5; }
.field__license-status { margin: 8px 0 0; font-size: 0.8125rem; font-weight: 600; }
.field__license-status--ok { color: #34d399; }
.field__license-status--warn { color: #fbbf24; }
.field__license-status--error { color: #f87171; }
.field__control { display: flex; justify-content: flex-start; }
.field__input {