feat: moonbase_licensing JUCE 8 module with native activation UI#14
Open
TobbenTM wants to merge 14 commits into
Open
feat: moonbase_licensing JUCE 8 module with native activation UI#14TobbenTM wants to merge 14 commits into
TobbenTM wants to merge 14 commits into
Conversation
Drop-in JUCE module (modules/moonbase_licensing) that talks to the Moonbase licensing API natively (not juce::OnlineUnlockStatus) with a built-in, brandable activation UI: online + offline activation, license details, deactivation, and online re-validation to refresh entitlements after a purchase. - Zero third-party deps: juce::WebInputStream transport, vendored nlohmann/json, OS-native RS256 verification (Security.framework / CNG / libcrypto) behind a compile-time backend selector that defaults to OpenSSL, so existing SDK consumers are unchanged. - Core SDK (gated, additive): detail/crypto backends, seat-count claims, validate_token_local_allow_expired, MOONBASE_DISABLE_CURL_TRANSPORT guard. - Quality of life: fail-loud config validation, an onDiagnostic sink, opt-in JUCE analytics/telemetry metadata, and expired-offline-license cleanup. - Build/test/CI: MOONBASE_USE_CURL / MOONBASE_SANITIZER options, native sample app, offscreen UI snapshot harness (Argos), JUCE controller test suite, and CI running the suites and sanitizers across macOS/Linux/Windows. The raw SDK target is renamed moonbase_cpp; the moonbase::licensing alias is preserved.
|
The latest updates on your projects. Learn more about Argos notifications ↗︎
|
Silence -Wshadow (StyledButton/LinkButton ctor params shadowed juce::Button::text) and -Wsign-conversion (url_encode iterated a string as unsigned char). Behavior unchanged; tests still green.
…erialization) - refreshLicense: persist on the message thread, gated by the generation check and serialized against deactivate()/clearLicense(), so an in-flight refresh can no longer recreate a just-cleared license on disk. The throttled path now passes a should_persist predicate to suppress the SDK's background-thread write. - der.hpp: bounds-check the TLV length against the remaining buffer before advancing, so a short-form length larger than the input is a configuration error instead of a read past the decoded key (Apple/Windows backends). - types.hpp: serialize seat_count / seats_used in the license JSON round trip so stored licenses keep their seat data. Tests: +2 JUCE (resurrection guard, malformed-DER rejection), +1/updated core (seat round trip). Core 57/57, JUCE 20/20.
choco install openssl flakes intermittently ('Value cannot be null'). Use the
vcpkg toolchain like ci.yml does for its Windows deps, which also auto-deploys
the OpenSSL DLLs next to the test executable.
An inline run: starting with a quoted scalar broke the workflow parse; use a run: | block like ci.yml does.
Routing keyed the trial screen on config.enableTrial, so a backend-granted trial fell through to the generic license-details view when the merchant had the 'Start trial' button disabled (e.g. the sample app). Decouple them: enableTrial now only governs the Welcome 'Start trial' affordance, while any active trial license routes to the trial view (applyLicense + showDetails via a shared screenForCurrentLicense() helper).
…ts in ActivationConfig Adds onlineCheckInterval (5 min default), onlineGracePeriod (7 days), and httpConnectTimeout/httpRequestTimeout to ActivationConfig, plumbed through toLicensingOptions(). These were the remaining licensing_options fields the module didn't surface (only target_platform stays auto-detected).
Instead of dropping an ended trial straight to the Welcome screen, route it to a new Expired view (from the design's "Trial expired" state): muted brand lockup, a red "Trial expired" pill, the end date, a full red bar, an "audio is bypassed" note, "Unlock full version", and "Activate offline instead". The plugin stays locked: license() remains empty (DSP gating keeps bypassing), while the ended trial is held in expiredTrial_ for display only. start() detects an expired trial on load and routes via showTrialExpired(); setPreviewState handles Screen::Expired the same way. Tests: controller test (expired trial -> Expired + license locked) and a 06b-trial-expired visual snapshot. JUCE 23/23, core 57/57.
Previously only a locally-expired trial showed the Expired screen; a trial that was still valid locally but expired per an online re-validation fell through to the locked/Welcome path. Now both start() and refreshLicense() catch license_expired_error from re-validation and, for a trial, route to the Expired screen (holding the trial we have, since the throw returns no token) while keeping the plugin locked. Non-trial expiry and network blips are unchanged. Tests: +2 JUCE (start() re-validates expired -> Expired; refreshLicense expired -> Expired + locked). JUCE 25/25.
The native module can't initiate a trial (trials are granted by the backend; there is no SDK call to start one), and the button's onClick just ran the same online-activation flow as "Activate online". Remove the button outright along with the now-pointless config.enableTrial flag, strings.startTrial / startTrialText(), and the trial mention in the default welcome copy. trialLengthDays + trialFeatures stay (the Trial / Expired screens display them). JUCE 25/25; the welcome snapshot is unchanged (the demo already had it off).
…rflow The features were drawn as fixed rows straight onto the view, so a long list ran under the buttons / clipped and a short list clustered at the top. Move them into a juce::Viewport with a TrialFeatureList content component: rows are evenly spaced and centred vertically when they fit, and the viewport scrolls (vertical scrollbar) when they overflow. Adds a 06c-trial-overflow visual snapshot.
Neither was read anywhere: the 'Manage license' button that consumed manageUrl was removed earlier, and supportUrl was never wired up. Remove the dead fields from ActivationConfig and the docs that advertised them, so the config surface only exposes options that do something.
Previously start()/poll/deactivate/refresh ran on detached juce::Thread::launch workers that were never joined, so a worker could keep executing module code (a blocking WebInputStream read, up to the connect timeout) after the controller - and, during plugin scanning / pluginval, the plugin binary - was destroyed. The WeakReference/generation guards prevented controller use-after-free but not this. Now: - juce_http_transport builds its WebInputStream directly and exposes cancel() (WebInputStream::cancel) to interrupt a blocking read from another thread. - ActivationController runs all async work on an owned juce::ThreadPool; the destructor calls the transport's cancel() then removeAllJobs(wait), so workers are unblocked and joined before anything is destroyed - no detached thread outlives the controller, and the drain is near-instant. Consumers can now call start() straight from the editor ctor and tear down at any time without deferring it. Adds a test that destroys the controller while a request is blocked and asserts it cancels + drains without hanging (JUCE 26/26).
Add licensing_options::client_info, a free-form token the base client appends to the User-Agent after "moonbase-cpp/<version>", so the server can tell which higher-level client made the request. The JUCE module fills it via toLicensingOptions() with "moonbase-juce/<version> (JUCE <v>; <os>)". Also define MOONBASE_LICENSING_VERSION / MOONBASE_CPP_VERSION in the module header: the SDK version macro only reached the moonbase_cpp target, so the module was reporting moonbase-cpp/0.0.0. Now it reports the real version. Tests: client_tests asserts client_info is appended to the UA; controller_tests assert the module fills client_info and that a real request carries moonbase-cpp/<ver> (not 0.0.0) + moonbase-juce/. Core 57/57, JUCE 27.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds
modules/moonbase_licensing, a drop-in JUCE 8 module that integrates the Moonbase licensing API natively (notjuce::OnlineUnlockStatus) with a brandable built-in activation UI covering online/offline activation, license details, deactivation, and online re-validation to refresh entitlements after a purchase. It ships zero third-party dependencies (JUCE WebInputStream transport, vendored nlohmann/json, OS-native RS256 verification via Security.framework/CNG/libcrypto) behind a compile-time backend selector that defaults to OpenSSL, so existing SDK consumers are unchanged. Core SDK changes are additive and gated: a crypto backend abstraction, seat-count claims,validate_token_local_allow_expired, and a curl-transport guard; the raw CMake target is renamedmoonbase_cppwith themoonbase::licensingalias preserved. It also adds fail-loud config validation, anonDiagnosticsink, opt-in JUCE telemetry metadata, expired-offline-license cleanup, a native sample app, an offscreen UI snapshot harness (Argos), and a JUCE controller test suite. New CMake options (MOONBASE_USE_CURL,MOONBASE_SANITIZER, JUCE build flags) and CI run the test suites and ASan/TSan across macOS, Linux, and Windows.