feat(license): registration + 1-year licenses + tier scaffolding
A complete offline licensing layer (no internet at any step): Core - src/license/ — schema (License, Tier, FeatureFlag), HMAC crypto, JSON storage, LicenseManager singleton with activate/renew/ deactivate/issue_trial. Tier-scaffolded so future SKUs can carve per-tool feature sets without consumer-code edits. - scripts/generate_license.py — creator-only key generator. Mints a DTLIC1: blob the buyer pastes into the activation page. GUI - New activation form component (src/gui/components/activation.py). - hide_streamlit_chrome() now inline-renders the activation form when no valid license is present (every page short-circuits to the form until activated). - Sidebar shows tier + days remaining; renewal warning under 30 days. - New pages/_Activate.py for revisiting the form after activation. CLI - src/license_cli.py — activate / renew / status / trial / deactivate commands. Exempt from the guard. - src/cli_license_guard.py — drop-in guard call added to every tool CLI's main(). Lets --help through; respects DATATOOLS_DEV_MODE. i18n - New activation.* and license.* keys in en.json + es.json (page title, form labels, status badges, renewal warnings, error messages). Pack parity test stays green. Test infrastructure - tests/conftest.py autouse fixture sets DATATOOLS_DEV_MODE=1 so the existing 1916 tests continue to pass. - isolated_license_path / activated_license_manager / unactivated_license_manager fixtures for tests that want to drive the real check. Tests (+79) - tests/test_license.py (40): schema, crypto roundtrip, blob encode/decode, tier→feature mapping, activation flow, name/email mismatch rejection, tamper detection, expiration, renewal, dev-mode bypass. - tests/test_license_cli.py (26): every license_cli command + subprocess tests confirming every tool CLI refuses to run without a license, --help always works, DEV_MODE bypasses. - tests/gui/test_activation.py (13): gate blocks without license, passes with trial, activation form submission unlocks the gate, sidebar status, renewal warning, i18n. Total: 1916 → 1995 tests. All pass under the strict warning filter. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -72,13 +72,21 @@ footer {
|
||||
"""
|
||||
|
||||
|
||||
def hide_streamlit_chrome() -> None:
|
||||
def hide_streamlit_chrome(*, gate_license: bool = True) -> None:
|
||||
"""Inject CSS to hide Streamlit's default header, menu, and footer.
|
||||
|
||||
Also renders the sidebar language selector, since every entrypoint
|
||||
that hides the default chrome wants the picker visible in the
|
||||
same place. Pages that want a clean chrome without the selector can
|
||||
inject ``_HIDE_CHROME_CSS`` themselves instead of calling this.
|
||||
Also renders the sidebar language selector + license status badge,
|
||||
since every entrypoint that hides the default chrome wants those
|
||||
visible in the same place. Pages that want a clean chrome without
|
||||
them can inject ``_HIDE_CHROME_CSS`` themselves instead of calling
|
||||
this.
|
||||
|
||||
When *gate_license* is True (the default) the function calls
|
||||
:func:`require_license_or_render_activation` after the sidebar
|
||||
widgets render. If no valid license is present, the activation
|
||||
form replaces the page body and the page short-circuits via
|
||||
``st.stop()``. The Activate page itself passes ``False`` so it
|
||||
can render its own form without recursion.
|
||||
"""
|
||||
st.markdown(_HIDE_CHROME_CSS, unsafe_allow_html=True)
|
||||
# Imported lazily so this module stays importable in environments
|
||||
@@ -86,6 +94,14 @@ def hide_streamlit_chrome() -> None:
|
||||
# individual legacy helpers).
|
||||
from src.i18n import render_language_selector
|
||||
render_language_selector()
|
||||
# License chrome: sidebar status badge + inline gate.
|
||||
from .activation import (
|
||||
render_license_status_sidebar,
|
||||
require_license_or_render_activation,
|
||||
)
|
||||
render_license_status_sidebar()
|
||||
if gate_license:
|
||||
require_license_or_render_activation()
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user