feat(license): add BSL license enforcement with device limit indicator

- Add LICENSE_DEVICES env var (default 250, matches BSL 1.1 free tier)
- Add /api/settings/license endpoint returning device count vs limit
- Header shows flashing red "502/500 licensed" badge when over limit
- About page shows license tier, device count, and over-limit warning
- Nothing is crippled — all features work regardless of device count
- Bump version to 9.7.1

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Jason Staack
2026-03-19 22:28:56 -05:00
parent 0142107e68
commit fdc8d9cb68
9 changed files with 81 additions and 5 deletions

View File

@@ -135,9 +135,13 @@ class Settings(BaseSettings):
# Retention cleanup — delete config snapshots older than N days
CONFIG_RETENTION_DAYS: int = 90
# Licensing — BSL 1.1 free tier allows up to 250 devices.
# Commercial license required above this limit. Set to 0 for unlimited.
LICENSE_DEVICES: int = 250
# App settings
APP_NAME: str = "TOD - The Other Dude"
APP_VERSION: str = "9.7.0"
APP_VERSION: str = "9.7.1"
DEBUG: bool = False
@field_validator("CREDENTIAL_ENCRYPTION_KEY")

View File

@@ -176,3 +176,24 @@ async def clear_winbox_sessions(user=Depends(require_role("super_admin"))):
return {"status": "ok", "deleted": deleted}
finally:
await rd.aclose()
# ---------------------------------------------------------------------------
# License status (any authenticated user)
# ---------------------------------------------------------------------------
@router.get("/license", summary="Get license status")
async def get_license_status():
"""Return current license tier, device limit, and actual device count."""
async with AdminAsyncSessionLocal() as session:
result = await session.execute(text("SELECT count(*)::int FROM devices"))
device_count = result.scalar() or 0
limit = settings.LICENSE_DEVICES
return {
"licensed_devices": limit,
"actual_devices": device_count,
"over_limit": limit > 0 and device_count > limit,
"tier": "commercial" if limit > 250 else "free",
}

View File

@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
[project]
name = "the-other-dude-backend"
version = "9.7.0"
version = "9.7.1"
description = "MikroTik Fleet Management Portal - Backend API"
requires-python = ">=3.12"
dependencies = [