feat(pro): generalize pro tier to multi-surface extension points
Move the pro mount decnet/services/pro/ -> decnet/pro/ so the Professional tier
can contribute to more than honeypots. The core wires each surface only when
decnet/pro/ is present (absence stays the entitlement gate):
* services — registry scans decnet/pro/services/ (was decnet/services/pro/)
* API routes — decnet/pro/routes.py exposes ROUTERS, mounted under /api/v1
* web pages — Vite aliases @pro to the pro frontend (community -> empty stub),
App.tsx maps proRoutes into <Route>s, Layout renders a
PROFESSIONAL nav group; both tree-shake out of the community build
Frontend gate mirrors the existing VITE_DECNET_DEVELOPER tree-shake pattern.
Tests: registry + router seams (backend), empty-stub contract (frontend).
This commit is contained in:
@@ -6,11 +6,11 @@ Auto-discovers all BaseService subclasses by importing every module in the
|
||||
services package. Adding a new service requires nothing beyond dropping a
|
||||
new .py file here that subclasses BaseService.
|
||||
|
||||
Professional-tier honeypots live in the optional ``decnet.services.pro``
|
||||
subpackage, which ships only in the Professional build (a private tree merged
|
||||
in at packaging time) and is absent from the open-core Community build. The
|
||||
registry scans it when present, so absence of the directory IS the entitlement
|
||||
gate — no licence check, no feature flag.
|
||||
Professional-tier honeypots live in the optional ``decnet.pro.services``
|
||||
subpackage (a clone of the private decnet-professional repo, mounted at
|
||||
``decnet/pro/`` and absent from the open-core Community build). The registry
|
||||
scans it when present, so absence of the directory IS the entitlement gate —
|
||||
no licence check, no feature flag.
|
||||
"""
|
||||
|
||||
import importlib
|
||||
@@ -40,12 +40,13 @@ def _load_plugins() -> None:
|
||||
continue
|
||||
importlib.import_module(f"decnet.services.{module_info.name}")
|
||||
# Professional build only: present == entitled. Community build has no pro/.
|
||||
pro_dir = package_dir / "pro"
|
||||
pro_dir = package_dir.parent / "pro" / "services"
|
||||
if pro_dir.is_dir():
|
||||
for mi in pkgutil.iter_modules([str(pro_dir)]):
|
||||
importlib.import_module(f"decnet.services.pro.{mi.name}")
|
||||
importlib.import_module(f"decnet.pro.services.{mi.name}")
|
||||
for cls in _all_subclasses(BaseService):
|
||||
if not cls.__module__.startswith("decnet.services."):
|
||||
mod = cls.__module__
|
||||
if not (mod.startswith("decnet.services.") or mod.startswith("decnet.pro.")):
|
||||
continue
|
||||
instance = cls() # type: ignore[abstract]
|
||||
_registry[instance.name] = instance
|
||||
|
||||
@@ -218,3 +218,13 @@ api_router.include_router(ttp_rules_router)
|
||||
api_router.include_router(ttp_tag_details_router)
|
||||
api_router.include_router(ttp_navigator_router)
|
||||
api_router.include_router(ttp_groups_for_technique_router)
|
||||
|
||||
# Professional tier (optional): mount pro routers under /api/v1 when the
|
||||
# decnet/pro/ package is present. Absence is the entitlement gate — community
|
||||
# builds have no decnet.pro, so this is a no-op there.
|
||||
try:
|
||||
from decnet.pro.routes import ROUTERS as _pro_routers
|
||||
except ModuleNotFoundError:
|
||||
_pro_routers = []
|
||||
for _pro_router in _pro_routers:
|
||||
api_router.include_router(_pro_router)
|
||||
|
||||
Reference in New Issue
Block a user