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:
co-authored by
Claude Sonnet 5
parent
eb03d3dd6c
commit
540083e6e4
+87
-22
@@ -1,6 +1,6 @@
|
||||
# Aurora Admin v2 — Project Status
|
||||
|
||||
_Last updated: 2026-07-03 (latest session — free/paid feature gating (licensing))_
|
||||
_Last updated: 2026-07-04 (latest session — real license-server integration)_
|
||||
|
||||
## What this project is
|
||||
|
||||
@@ -65,6 +65,20 @@ get back up to speed instead of relying on prior chat history.
|
||||
- **Git**: local repo config only (`user.name`/`user.email` set per-repo, not
|
||||
`--global`). Commit after each user-confirmed-working change, not before.
|
||||
- Site under test: `http://admin.local/wp-admin/`.
|
||||
- **License server access**: `auroraplugins.com` runs on the same cPanel
|
||||
box as the git remote, `auroradragon.studio` (hostname
|
||||
`wyvern.auroradragon.studio`), but the git remote's SSH key
|
||||
(`~/.ssh/id_ed25519`) is scoped to git-only — no shell. Root shell access
|
||||
is a separate dedicated key, `~/.ssh/id_ed25519_auroradragon_root`
|
||||
(`ssh -i ~/.ssh/id_ed25519_auroradragon_root [email protected]`).
|
||||
The license server itself is the `aurora-licensing` WordPress plugin at
|
||||
`/home/aurora/public_html/wp-content/plugins/aurora-licensing/aurora-licensing.php`
|
||||
(also present under `/home/plugins/public_html/...` — a second copy on
|
||||
the same box). **If SSH to port 22 suddenly times out**, something on the
|
||||
box (fail2ban or similar) appears to re-trigger a short block on rapid
|
||||
repeated connection attempts, including plain `nc`/probe connections —
|
||||
it clears on its own after roughly 1-2 minutes; wait it out with one
|
||||
careful attempt rather than retrying in a tight loop.
|
||||
|
||||
## Architecture
|
||||
|
||||
@@ -449,13 +463,56 @@ Creator, Admin Notices, Comments, and the Plugins page stay free.
|
||||
- `admin/src/Licensing/License.php` is the single gate every paid feature
|
||||
checks — `PAID_FEATURES` maps a feature id to a human label,
|
||||
`is_active()` and `is_feature_available($feature)` are the two calls
|
||||
everything else uses. **`is_active()` is a placeholder today**: any
|
||||
non-empty `license_key` setting counts as licensed, no remote validation
|
||||
at all. There's no license server yet — when one exists, only
|
||||
`is_active()`'s internals need to change (ideally caching the remote
|
||||
check in a transient so it isn't a network call on every admin page
|
||||
load); every gated feature already funnels through this class rather than
|
||||
reading the setting directly, so nothing else needs to change.
|
||||
everything else uses.
|
||||
- **Real license-server integration** (this session, supersedes the earlier
|
||||
"any non-empty key counts as active" placeholder): validates against the
|
||||
actual license server — the `aurora-licensing` WordPress plugin running
|
||||
on `auroraplugins.com` (a cPanel box at `auroradragon.studio`, SSH-
|
||||
accessible as root via a dedicated key,
|
||||
`~/.ssh/id_ed25519_auroradragon_root` — the git-remote key is scoped
|
||||
git-only and can't get a shell). REST namespace
|
||||
`/wp-json/aurora-licensing/v1/`:
|
||||
- `POST /validate` — `{license_key, domain, product_id?}` →
|
||||
`{valid, status, plan, max_sites, active_sites, active_on_domain,
|
||||
expires_at, product_id}` on success, `{valid:false, error, message}`
|
||||
on failure (e.g. `invalid_key`).
|
||||
- `POST /activate` — same request shape → `{activated, plan, max_sites,
|
||||
expires_at}`, or `{activated:true, message:"Already activated..."}` if
|
||||
already active on this domain, or a 403 `{valid:false,
|
||||
error:"limit_reached", message}` if `max_sites` is exceeded.
|
||||
- `POST /deactivate` — `{license_key, domain}` → `{deactivated, message}`.
|
||||
- `domain` is normalized server-side (strips protocol/`www.`/trailing
|
||||
slash) — `License::domain()` just sends the bare host from `home_url()`.
|
||||
- `GET /my-licenses` (cookie-auth, lists all of a customer's licenses) is
|
||||
the account-portal-facing endpoint, not used by the plugin itself.
|
||||
- `License::status()` caches the `/validate` response in the
|
||||
`aurora_admin_license_status` option for 12 hours
|
||||
(`STATUS_TTL`) — `is_active()`/`is_feature_available()` read this
|
||||
cache rather than hitting the network on every admin page load.
|
||||
- `License::on_settings_updated()` hooks `update_option_aurora_admin_settings`
|
||||
(same hook `ActivityLog.php` uses) and re-activates/deactivates
|
||||
immediately whenever `license_key` actually changes, rather than
|
||||
waiting out the cache TTL — deactivates the old key's slot on this
|
||||
domain before activating the new one, so switching keys doesn't leak
|
||||
an activation. Instantiated in `AuroraAdmin.php` (`new License();`)
|
||||
so this hook actually registers — it wasn't needed when the class was
|
||||
static-only.
|
||||
- Network failures/non-JSON responses collapse into a synthetic
|
||||
`{valid:false, status:"unreachable", ...}` shape (`License::call()`)
|
||||
so callers only ever branch on response fields, never on `WP_Error`.
|
||||
- **Settings UI feedback**: `SettingsPage.php` passes `License::status()`
|
||||
to the Vue settings page as `licenseStatus`; `Settings.vue` forwards it
|
||||
only to the `license_key` field; `SettingsField.vue` shows a colored
|
||||
status line under the field (green "License active (plan) — expires
|
||||
X", amber "unreachable, will retry", red the server's own error
|
||||
message) — verified live against the real server with a fake test key
|
||||
and got back the exact `{"valid":false,"error":"invalid_key","message":
|
||||
"License key not found."}` shape.
|
||||
- **Not yet done**: no real license key was available to test the
|
||||
activate/site-limit-reached path end-to-end (only the invalid-key path
|
||||
was verified against the live server) — worth a follow-up pass once a
|
||||
real test key exists. No UI yet for the `limit_reached` case beyond
|
||||
surfacing its message string.
|
||||
- **Sub-apps** (`SubApps.php`): `PAGES` now has a `"feature"` key per page
|
||||
(`null` for the 4 free ones). Locked pages still fully register
|
||||
(`add_submenu_page`, real capability, real URL — same as before) so nav/
|
||||
@@ -614,15 +671,22 @@ scrollbar with a large empty gap below the frame. Also made the sidebar's
|
||||
native-menu sections a single-open accordion instead of letting multiple
|
||||
stay expanded at once (pushed as commit `73591e0`).
|
||||
|
||||
Most recently: added the free/paid feature-gating system end-to-end (see
|
||||
"Licensing" above) — Role Editor, Activity Log, Database Explorer,
|
||||
Analytics, White Label, and the remote site switcher are now paid; Menu
|
||||
Creator, Admin Notices, Comments, Plugins, and the whole shell/theming
|
||||
product stay free. Verified both directions live (entering/clearing a
|
||||
license key immediately locks/unlocks the sidebar badges, the sub-app
|
||||
pages, the Dashboard's Analytics tab, and the underlying REST endpoints),
|
||||
and fixed an unrelated pre-existing 500 in Role Editor's REST endpoint
|
||||
along the way (pushed as commit `6d50260`). The distributable zip
|
||||
An earlier pass this session added the free/paid feature-gating system
|
||||
end-to-end (see "Licensing" above) — Role Editor, Activity Log, Database
|
||||
Explorer, Analytics, White Label, and the remote site switcher are now
|
||||
paid; Menu Creator, Admin Notices, Comments, Plugins, and the whole
|
||||
shell/theming product stay free. That pass used a placeholder
|
||||
`is_active()` (any non-empty key counted as licensed) and fixed an
|
||||
unrelated pre-existing 500 in Role Editor's REST endpoint along the way
|
||||
(pushed as commit `6d50260`).
|
||||
|
||||
Most recently: replaced that placeholder with real validation against the
|
||||
license server at `auroraplugins.com` (see "Real license-server
|
||||
integration" above) — `/validate`/`/activate`/`/deactivate` calls, 12-hour
|
||||
cached status, immediate re-check on a license-key change, and a status
|
||||
line in the Settings UI. Verified live end-to-end against the real server
|
||||
with a fake test key, which correctly came back locked with the server's
|
||||
own `"License key not found."` message. The distributable zip
|
||||
(`dist/aurora-admin-v2.zip`) was rebuilt fresh from source and should be
|
||||
re-verified/re-zipped again after this doc update if anything changed since.
|
||||
|
||||
@@ -632,8 +696,9 @@ hot-patched into
|
||||
the repo's source files are the actual edits; that copy is just the
|
||||
compiled build artifact, not a separate edit surface).
|
||||
|
||||
**Not yet built**: an actual license-purchase/checkout flow or remote
|
||||
validation server — today entering *any* non-empty text in the License key
|
||||
field unlocks every paid feature (see "Licensing" above). This is fine for
|
||||
internal testing but must be replaced with real validation before this
|
||||
ships to real users, or the paid tier isn't actually gated.
|
||||
**Not yet done**: the activate/site-limit-reached path hasn't been tested
|
||||
with a real, valid license key (only the invalid-key path was verified
|
||||
against the live server) — worth a follow-up pass once a real test key is
|
||||
available. No purchase/checkout flow lives in this repo (that's entirely
|
||||
on the `auroraplugins.com`/WooCommerce side, which auto-generates keys on
|
||||
order completion) — this repo only ever consumes the license API.
|
||||
|
||||
Reference in New Issue
Block a user