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:
270
server/CLAUDE.md
270
server/CLAUDE.md
@@ -1,212 +1,76 @@
|
||||
# Claude Instructions for WLED Screen Controller Server
|
||||
|
||||
## Development Workflow
|
||||
## Project Structure
|
||||
|
||||
### Server Restart Policy
|
||||
- `src/wled_controller/main.py` — FastAPI application entry point
|
||||
- `src/wled_controller/api/routes/` — REST API endpoints (one file per entity)
|
||||
- `src/wled_controller/api/schemas/` — Pydantic request/response models (one file per entity)
|
||||
- `src/wled_controller/core/` — Core business logic (capture, devices, audio, processing, automations)
|
||||
- `src/wled_controller/storage/` — Data models (dataclasses) and JSON persistence stores
|
||||
- `src/wled_controller/utils/` — Utility functions (logging, monitor detection)
|
||||
- `src/wled_controller/static/` — Frontend files (TypeScript, CSS, locales)
|
||||
- `src/wled_controller/templates/` — Jinja2 HTML templates
|
||||
- `config/` — Configuration files (YAML)
|
||||
- `data/` — Runtime data (JSON stores, persisted state)
|
||||
|
||||
**IMPORTANT**: When making changes to server code (Python files in `src/wled_controller/`), you MUST restart the server if it's currently running to ensure the changes take effect.
|
||||
## Entity & Storage Pattern
|
||||
|
||||
**NOTE**: Auto-reload is currently disabled (`reload=False` in `main.py`) due to watchfiles causing an infinite reload loop. Changes to server code will NOT be automatically picked up - manual server restart is required.
|
||||
|
||||
#### When to restart:
|
||||
- After modifying API routes (`api/routes.py`, `api/schemas.py`)
|
||||
- After updating core logic (`core/*.py`)
|
||||
- After changing configuration (`config.py`)
|
||||
- After modifying utilities (`utils/*.py`)
|
||||
- After updating data models or database schemas
|
||||
|
||||
#### How to check if server is running:
|
||||
```bash
|
||||
# Look for running Python processes with wled_controller
|
||||
ps aux | grep wled_controller
|
||||
# Or check for processes listening on port 8080
|
||||
netstat -an | grep 8080
|
||||
```
|
||||
|
||||
#### How to restart:
|
||||
1. **Find the task ID** of the running server (look for background bash tasks in conversation)
|
||||
2. **Stop the server** using TaskStop with the task ID
|
||||
3. **Check for port conflicts** (port 8080 may still be in use):
|
||||
```bash
|
||||
netstat -ano | findstr :8080
|
||||
```
|
||||
If a process is still using port 8080, kill it:
|
||||
```bash
|
||||
powershell -Command "Stop-Process -Id <PID> -Force"
|
||||
```
|
||||
4. **Start a new server instance** in the background:
|
||||
```bash
|
||||
cd server && python -m wled_controller.main
|
||||
```
|
||||
Use `run_in_background: true` parameter in Bash tool
|
||||
5. **Wait 3 seconds** for server to initialize:
|
||||
```bash
|
||||
sleep 3
|
||||
```
|
||||
6. **Verify startup** by reading the output file:
|
||||
- Look for "Uvicorn running on http://0.0.0.0:8080"
|
||||
- Check for any errors in stderr
|
||||
- Verify "Application startup complete" message
|
||||
|
||||
**Common Issues:**
|
||||
- **Port 8080 in use**: Old process didn't terminate cleanly - kill it manually
|
||||
- **Module import errors**: Check that all Python files are syntactically correct
|
||||
- **Permission errors**: Ensure file permissions allow Python to execute
|
||||
|
||||
#### Files that DON'T require restart:
|
||||
- Static files (`static/*.html`, `static/*.css`, `static/*.js`) - but you **MUST rebuild the bundle** after changes: `cd server && npm run build`
|
||||
- Locale files (`static/locales/*.json`) - loaded by frontend
|
||||
- Documentation files (`*.md`)
|
||||
- Configuration files in `config/` if server supports hot-reload (check implementation)
|
||||
|
||||
### Git Commit and Push Policy
|
||||
|
||||
**CRITICAL**: NEVER commit OR push code changes without explicit user approval.
|
||||
|
||||
#### Rules
|
||||
|
||||
- You MUST NOT create commits without explicit user instruction
|
||||
- You MUST NOT push commits unless explicitly instructed by the user
|
||||
- Wait for the user to review changes and ask you to commit
|
||||
- If the user says "commit", create a commit but DO NOT push
|
||||
- If the user says "commit and push", you may push after committing
|
||||
- Always wait for explicit permission before any commit or push operation
|
||||
|
||||
#### Workflow
|
||||
|
||||
1. Make changes to code
|
||||
2. **STOP and WAIT** - inform the user of changes and wait for instruction
|
||||
3. Only create commit when user explicitly requests it (e.g., "commit", "create a commit")
|
||||
4. **STOP and WAIT** - do not push
|
||||
5. Only push when user explicitly requests it (e.g., "push", "commit and push", "push to remote")
|
||||
|
||||
### Testing Changes
|
||||
|
||||
After restarting the server with new code:
|
||||
1. Test the modified endpoints/functionality
|
||||
2. Check browser console for any JavaScript errors
|
||||
3. Verify API responses match updated schemas
|
||||
4. Test with different locales if i18n was modified
|
||||
|
||||
## Project Structure Notes
|
||||
|
||||
- `src/wled_controller/main.py` - FastAPI application entry point
|
||||
- `src/wled_controller/api/` - REST API endpoints and schemas
|
||||
- `src/wled_controller/core/` - Core business logic (screen capture, WLED client, processing)
|
||||
- `src/wled_controller/utils/` - Utility functions (logging, monitor detection)
|
||||
- `src/wled_controller/static/` - Frontend files (HTML, CSS, JS, locales)
|
||||
- `config/` - Configuration files (YAML)
|
||||
- `data/` - Runtime data (devices.json, persistence)
|
||||
|
||||
## Common Tasks
|
||||
|
||||
### Adding a new API endpoint:
|
||||
1. Add route to `api/routes.py`
|
||||
2. Define request/response schemas in `api/schemas.py`
|
||||
3. **Restart the server**
|
||||
4. Test the endpoint via `/docs` (Swagger UI)
|
||||
|
||||
### Adding a new field to existing API:
|
||||
1. Update Pydantic schema in `api/schemas.py`
|
||||
2. Update corresponding dataclass (if applicable)
|
||||
3. Update backend logic to populate the field
|
||||
4. **Restart the server**
|
||||
5. Update frontend to display the new field
|
||||
|
||||
### Modifying display/monitor detection:
|
||||
1. Update functions in `utils/monitor_names.py` or `core/screen_capture.py`
|
||||
2. **Restart the server**
|
||||
3. Test with `GET /api/v1/config/displays`
|
||||
|
||||
### Modifying server login:
|
||||
1. Update the logic.
|
||||
2. **Restart the server**
|
||||
|
||||
### Adding translations:
|
||||
1. Add keys to `static/locales/en.json` and `static/locales/ru.json`
|
||||
2. Add `data-i18n` attributes to HTML elements in `static/index.html`
|
||||
3. Use `t('key')` function in `static/app.js` for dynamic content
|
||||
4. No server restart needed (frontend only)
|
||||
|
||||
## Frontend UI Patterns
|
||||
|
||||
### Entity Cards
|
||||
|
||||
All entity cards (devices, targets, CSS sources, streams, scenes, automations, etc.) **must support clone functionality**. Clone buttons use the `ICON_CLONE` (📋) icon in `.card-actions`.
|
||||
|
||||
**Clone pattern**: Clone must open the entity's add/create modal with fields prefilled from the cloned item. It must **never** silently create a duplicate — the user should review and confirm.
|
||||
|
||||
Implementation:
|
||||
|
||||
1. Export a `cloneMyEntity(id)` function that fetches (or finds in cache) the entity data
|
||||
2. Call the add/create modal function, passing the entity data as `cloneData`
|
||||
3. In the modal opener, detect clone mode (no ID + cloneData present) and prefill all fields
|
||||
4. Append `' (Copy)'` to the name
|
||||
5. Set the modal title to the "add" variant (not "edit")
|
||||
6. The save action creates a new entity (POST), not an update (PUT)
|
||||
|
||||
```javascript
|
||||
export async function cloneMyEntity(id) {
|
||||
const entity = myCache.data.find(e => e.id === id);
|
||||
if (!entity) return;
|
||||
showMyEditor(null, entity); // null id = create mode, entity = cloneData
|
||||
}
|
||||
```
|
||||
|
||||
Register the clone function in `app.js` window exports so inline `onclick` handlers can call it.
|
||||
|
||||
### Modal Dialogs
|
||||
|
||||
**IMPORTANT**: All modal dialogs must follow these standards for consistent UX:
|
||||
|
||||
#### Backdrop Click Behavior
|
||||
All modals MUST close when the user clicks outside the dialog (on the backdrop). Implement this by adding a click handler that checks if the clicked element is the modal backdrop itself:
|
||||
|
||||
```javascript
|
||||
// Show modal
|
||||
const modal = document.getElementById('my-modal');
|
||||
modal.style.display = 'flex';
|
||||
|
||||
// Add backdrop click handler to close modal
|
||||
modal.onclick = function(event) {
|
||||
if (event.target === modal) {
|
||||
closeMyModal();
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
**Where to add**: In every function that shows a modal (e.g., `showAddTemplateModal()`, `editTemplate()`, `showTestTemplateModal()`).
|
||||
|
||||
#### Close Button Requirement
|
||||
Each modal dialog that has a "Cancel" button MUST also have a cross (×) close button at the top-right corner of the dialog. This provides users with multiple intuitive ways to dismiss the dialog:
|
||||
|
||||
1. Click the backdrop (outside the dialog)
|
||||
2. Click the × button (top-right corner)
|
||||
3. Click the Cancel button (bottom of dialog)
|
||||
4. Press Escape key (if implemented)
|
||||
|
||||
**HTML Structure**:
|
||||
```html
|
||||
<div class="modal-content">
|
||||
<button class="close-btn" onclick="closeMyModal()">×</button>
|
||||
<h2>Dialog Title</h2>
|
||||
<!-- dialog content -->
|
||||
<div class="modal-actions">
|
||||
<button onclick="closeMyModal()">Cancel</button>
|
||||
<button onclick="submitAction()">Submit</button>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
**CSS Requirements**:
|
||||
- Close button should be positioned absolutely at top-right
|
||||
- Should be easily clickable (min 24px × 24px hit area)
|
||||
- Should have clear hover state
|
||||
Each entity follows: dataclass model (`storage/`) + JSON store (`storage/*_store.py`) + Pydantic schemas (`api/schemas/`) + routes (`api/routes/`).
|
||||
|
||||
## Authentication
|
||||
|
||||
Server uses API key authentication. Keys are configured in:
|
||||
- `config/default_config.yaml` under `auth.api_keys`
|
||||
- Or via environment variables: `WLED_AUTH__API_KEYS`
|
||||
Server uses API key authentication via Bearer token in `Authorization` header.
|
||||
|
||||
For development, ensure at least one API key is configured or the server won't start.
|
||||
- Config: `config/default_config.yaml` under `auth.api_keys`
|
||||
- Env var: `WLED_AUTH__API_KEYS`
|
||||
- Dev key: `development-key-change-in-production`
|
||||
|
||||
## Common Tasks
|
||||
|
||||
### Adding a new API endpoint
|
||||
|
||||
1. Create route file in `api/routes/`
|
||||
2. Define request/response schemas in `api/schemas/`
|
||||
3. Register the router in `main.py`
|
||||
4. Restart the server
|
||||
5. Test via `/docs` (Swagger UI)
|
||||
|
||||
### Adding a new field to existing API
|
||||
|
||||
1. Update Pydantic schema in `api/schemas/`
|
||||
2. Update corresponding dataclass in `storage/`
|
||||
3. Update backend logic to populate the field
|
||||
4. Restart the server
|
||||
5. Update frontend to display the new field
|
||||
6. Rebuild bundle: `cd server && npm run build`
|
||||
|
||||
### Adding translations
|
||||
|
||||
1. Add keys to `static/locales/en.json`, `static/locales/ru.json`, and `static/locales/zh.json`
|
||||
2. Add `data-i18n` attributes to HTML elements in `templates/`
|
||||
3. Use `t('key')` in TypeScript modules (`static/js/`)
|
||||
4. No server restart needed (frontend only), but rebuild bundle if JS changed
|
||||
|
||||
### Modifying display/monitor detection
|
||||
|
||||
1. Update functions in `utils/monitor_names.py` or `core/screen_capture.py`
|
||||
2. Restart the server
|
||||
3. Test with `GET /api/v1/config/displays`
|
||||
|
||||
## Testing
|
||||
|
||||
```bash
|
||||
cd server && pytest # Run all tests
|
||||
cd server && pytest --cov # With coverage report
|
||||
cd server && pytest tests/test_api.py # Single test file
|
||||
```
|
||||
|
||||
Tests are in `server/tests/`. Config in `pyproject.toml` under `[tool.pytest]`.
|
||||
|
||||
## Frontend Conventions
|
||||
|
||||
For all frontend conventions (CSS variables, UI patterns, modals, localization, icons, bundling), see [contexts/frontend.md](../contexts/frontend.md).
|
||||
|
||||
## Server Operations
|
||||
|
||||
For restart procedures, server modes, and demo mode checklist, see [contexts/server-operations.md](../contexts/server-operations.md).
|
||||
|
||||
Reference in New Issue
Block a user