refactor: comprehensive code quality, security, and release readiness improvements
Some checks failed
Lint & Test / test (push) Failing after 48s
Some checks failed
Lint & Test / test (push) Failing after 48s
Security: tighten CORS defaults, add webhook rate limiting, fix XSS in automations, guard WebSocket JSON.parse, validate ADB address input, seal debug exception leak, URL-encode WS tokens, CSS.escape in selectors. Code quality: add Pydantic models for brightness/power endpoints, fix thread safety and name uniqueness in DeviceStore, immutable update pattern, split 6 oversized files into 16 focused modules, enable TypeScript strictNullChecks (741→102 errors), type state variables, add dom-utils helper, migrate 3 modules from inline onclick to event delegation, ProcessorDependencies dataclass. Performance: async store saves, health endpoint log level, command palette debounce, optimized entity-events comparison, fix service worker precache list. Testing: expand from 45 to 293 passing tests — add store tests (141), route tests (25), core logic tests (42), E2E flow tests (33), organize into tests/api/, tests/storage/, tests/core/, tests/e2e/. DevOps: CI test pipeline, pre-commit config, Dockerfile multi-stage build with non-root user and health check, docker-compose improvements, version bump to 0.2.0. Docs: rewrite CLAUDE.md (202→56 lines), server/CLAUDE.md (212→76), create contexts/server-operations.md, fix .js→.ts references, fix env var prefix in README, rewrite INSTALLATION.md, add CONTRIBUTING.md and .env.example.
This commit is contained in:
@@ -16,6 +16,7 @@ from wled_controller.api.dependencies import (
|
||||
get_processor_manager,
|
||||
)
|
||||
from wled_controller.api.schemas.devices import (
|
||||
BrightnessRequest,
|
||||
DeviceCreate,
|
||||
DeviceListResponse,
|
||||
DeviceResponse,
|
||||
@@ -25,6 +26,7 @@ from wled_controller.api.schemas.devices import (
|
||||
DiscoverDevicesResponse,
|
||||
OpenRGBZoneResponse,
|
||||
OpenRGBZonesResponse,
|
||||
PowerRequest,
|
||||
)
|
||||
from wled_controller.core.processing.processor_manager import ProcessorManager
|
||||
from wled_controller.storage import DeviceStore
|
||||
@@ -53,18 +55,19 @@ def _device_to_response(device) -> DeviceResponse:
|
||||
zone_mode=device.zone_mode,
|
||||
capabilities=sorted(get_device_capabilities(device.device_type)),
|
||||
tags=device.tags,
|
||||
dmx_protocol=getattr(device, 'dmx_protocol', 'artnet'),
|
||||
dmx_start_universe=getattr(device, 'dmx_start_universe', 0),
|
||||
dmx_start_channel=getattr(device, 'dmx_start_channel', 1),
|
||||
espnow_peer_mac=getattr(device, 'espnow_peer_mac', ''),
|
||||
espnow_channel=getattr(device, 'espnow_channel', 1),
|
||||
hue_username=getattr(device, 'hue_username', ''),
|
||||
hue_client_key=getattr(device, 'hue_client_key', ''),
|
||||
hue_entertainment_group_id=getattr(device, 'hue_entertainment_group_id', ''),
|
||||
spi_speed_hz=getattr(device, 'spi_speed_hz', 800000),
|
||||
spi_led_type=getattr(device, 'spi_led_type', 'WS2812B'),
|
||||
chroma_device_type=getattr(device, 'chroma_device_type', 'chromalink'),
|
||||
gamesense_device_type=getattr(device, 'gamesense_device_type', 'keyboard'),
|
||||
dmx_protocol=device.dmx_protocol,
|
||||
dmx_start_universe=device.dmx_start_universe,
|
||||
dmx_start_channel=device.dmx_start_channel,
|
||||
espnow_peer_mac=device.espnow_peer_mac,
|
||||
espnow_channel=device.espnow_channel,
|
||||
hue_username=device.hue_username,
|
||||
hue_client_key=device.hue_client_key,
|
||||
hue_entertainment_group_id=device.hue_entertainment_group_id,
|
||||
spi_speed_hz=device.spi_speed_hz,
|
||||
spi_led_type=device.spi_led_type,
|
||||
chroma_device_type=device.chroma_device_type,
|
||||
gamesense_device_type=device.gamesense_device_type,
|
||||
default_css_processing_template_id=device.default_css_processing_template_id,
|
||||
created_at=device.created_at,
|
||||
updated_at=device.updated_at,
|
||||
)
|
||||
@@ -508,7 +511,7 @@ async def get_device_brightness(
|
||||
@router.put("/api/v1/devices/{device_id}/brightness", tags=["Settings"])
|
||||
async def set_device_brightness(
|
||||
device_id: str,
|
||||
body: dict,
|
||||
body: BrightnessRequest,
|
||||
_auth: AuthRequired,
|
||||
store: DeviceStore = Depends(get_device_store),
|
||||
manager: ProcessorManager = Depends(get_processor_manager),
|
||||
@@ -521,9 +524,7 @@ async def set_device_brightness(
|
||||
if "brightness_control" not in get_device_capabilities(device.device_type):
|
||||
raise HTTPException(status_code=400, detail=f"Brightness control is not supported for {device.device_type} devices")
|
||||
|
||||
bri = body.get("brightness")
|
||||
if bri is None or not isinstance(bri, int) or not 0 <= bri <= 255:
|
||||
raise HTTPException(status_code=400, detail="brightness must be an integer 0-255")
|
||||
bri = body.brightness
|
||||
|
||||
try:
|
||||
try:
|
||||
@@ -581,7 +582,7 @@ async def get_device_power(
|
||||
@router.put("/api/v1/devices/{device_id}/power", tags=["Settings"])
|
||||
async def set_device_power(
|
||||
device_id: str,
|
||||
body: dict,
|
||||
body: PowerRequest,
|
||||
_auth: AuthRequired,
|
||||
store: DeviceStore = Depends(get_device_store),
|
||||
manager: ProcessorManager = Depends(get_processor_manager),
|
||||
@@ -594,9 +595,7 @@ async def set_device_power(
|
||||
if "power_control" not in get_device_capabilities(device.device_type):
|
||||
raise HTTPException(status_code=400, detail=f"Power control is not supported for {device.device_type} devices")
|
||||
|
||||
on = body.get("on")
|
||||
if on is None or not isinstance(on, bool):
|
||||
raise HTTPException(status_code=400, detail="'on' must be a boolean")
|
||||
on = body.power
|
||||
|
||||
try:
|
||||
# For serial devices, use the cached idle client to avoid port conflicts
|
||||
|
||||
Reference in New Issue
Block a user