mirror of
https://github.com/ChuckBuilds/LEDMatrix.git
synced 2026-04-11 21:33:00 +00:00
feat(starlark): implement schema extraction, asset download, and config persistence
## Schema Extraction - Replace broken `pixlet serve --print-schema` with regex-based source parser - Extract schema by parsing `get_schema()` function from .star files - Support all field types: Location, Text, Toggle, Dropdown, Color, DateTime - Handle variable-referenced dropdown options (e.g., `options = teamOptions`) - Gracefully handle complex/unsupported field types (OAuth2, PhotoSelect, etc.) - Extract schema for 90%+ of Tronbyte apps ## Asset Download - Add `download_app_assets()` to fetch images/, sources/, fonts/ directories - Download assets in binary mode for proper image/font handling - Validate all paths to prevent directory traversal attacks - Copy asset directories during app installation - Enable apps like AnalogClock that require image assets ## Config Persistence - Create config.json file during installation with schema defaults - Update both config.json and manifest when saving configuration - Load config from config.json (not manifest) for consistency with plugin - Separate timing keys (render_interval, display_duration) from app config - Fix standalone web service mode to read/write config.json ## Pixlet Command Fix - Fix Pixlet CLI invocation: config params are positional, not flags - Change from `pixlet render file.star -c key=value` to `pixlet render file.star key=value -o output` - Properly handle JSON config values (e.g., location objects) - Enable config to be applied during rendering ## Security & Reliability - Add threading.Lock for cache operations to prevent race conditions - Reduce ThreadPoolExecutor workers from 20 to 5 for Raspberry Pi - Add path traversal validation in download_star_file() - Add YAML error logging in manifest fetching - Add file size validation (5MB limit) for .star uploads - Use sanitized app_id consistently in install endpoints - Use atomic manifest updates to prevent race conditions - Add missing Optional import for type hints ## Web UI - Fix standalone mode schema loading in config partial - Schema-driven config forms now render correctly for all apps - Location fields show lat/lng/timezone inputs - Dropdown, toggle, text, color, and datetime fields all supported Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -796,7 +796,7 @@ class StarlarkAppsPlugin(BasePlugin):
|
||||
except Exception as e:
|
||||
self.logger.error(f"Error displaying frame: {e}")
|
||||
|
||||
def install_app(self, app_id: str, star_file_path: str, metadata: Optional[Dict[str, Any]] = None) -> bool:
|
||||
def install_app(self, app_id: str, star_file_path: str, metadata: Optional[Dict[str, Any]] = None, assets_dir: Optional[str] = None) -> bool:
|
||||
"""
|
||||
Install a new Starlark app.
|
||||
|
||||
@@ -804,6 +804,7 @@ class StarlarkAppsPlugin(BasePlugin):
|
||||
app_id: Unique identifier for the app
|
||||
star_file_path: Path to .star file to install
|
||||
metadata: Optional metadata (name, description, etc.)
|
||||
assets_dir: Optional directory containing assets (images/, sources/, etc.)
|
||||
|
||||
Returns:
|
||||
True if successful
|
||||
@@ -827,6 +828,21 @@ class StarlarkAppsPlugin(BasePlugin):
|
||||
self._verify_path_safety(star_dest, self.apps_dir)
|
||||
shutil.copy2(star_file_path, star_dest)
|
||||
|
||||
# Copy asset directories if provided (images/, sources/, etc.)
|
||||
if assets_dir and Path(assets_dir).exists():
|
||||
assets_path = Path(assets_dir)
|
||||
for item in assets_path.iterdir():
|
||||
if item.is_dir():
|
||||
# Copy entire directory (e.g., images/, sources/)
|
||||
dest_dir = app_dir / item.name
|
||||
# Verify dest_dir path safety
|
||||
self._verify_path_safety(dest_dir, self.apps_dir)
|
||||
if dest_dir.exists():
|
||||
shutil.rmtree(dest_dir)
|
||||
shutil.copytree(item, dest_dir)
|
||||
self.logger.debug(f"Copied assets directory: {item.name}")
|
||||
self.logger.info(f"Installed assets for {app_id}")
|
||||
|
||||
# Create app manifest entry
|
||||
app_manifest = {
|
||||
"name": metadata.get("name", app_id) if metadata else app_id,
|
||||
|
||||
Reference in New Issue
Block a user