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:
@@ -215,7 +215,7 @@ export async function turnOffDevice(deviceId: any) {
|
||||
}
|
||||
|
||||
export async function pingDevice(deviceId: any) {
|
||||
const btn = document.querySelector(`[data-device-id="${deviceId}"] .card-ping-btn`) as HTMLElement | null;
|
||||
const btn = document.querySelector(`[data-device-id="${CSS.escape(deviceId)}"] .card-ping-btn`) as HTMLElement | null;
|
||||
if (btn) btn.classList.add('spinning');
|
||||
try {
|
||||
const resp = await fetchWithAuth(`/devices/${deviceId}/ping`, { method: 'POST' });
|
||||
@@ -403,7 +403,7 @@ export async function showSettings(deviceId: any) {
|
||||
const { baseUrl, zones: currentZones } = _splitOpenrgbZone(device.url);
|
||||
// Set zone mode radio from device
|
||||
const savedMode = device.zone_mode || 'combined';
|
||||
const modeRadio = document.querySelector(`input[name="settings-zone-mode"][value="${savedMode}"]`) as HTMLInputElement | null;
|
||||
const modeRadio = document.querySelector(`input[name="settings-zone-mode"][value="${CSS.escape(savedMode)}"]`) as HTMLInputElement | null;
|
||||
if (modeRadio) modeRadio.checked = true;
|
||||
_fetchOpenrgbZones(baseUrl, 'settings-zone-list', currentZones).then(() => {
|
||||
// Re-snapshot after zones are loaded so dirty-check baseline includes them
|
||||
@@ -536,7 +536,7 @@ export async function saveDeviceSettings() {
|
||||
|
||||
// Brightness
|
||||
export function updateBrightnessLabel(deviceId: any, value: any) {
|
||||
const slider = document.querySelector(`[data-device-brightness="${deviceId}"]`) as HTMLElement | null;
|
||||
const slider = document.querySelector(`[data-device-brightness="${CSS.escape(deviceId)}"]`) as HTMLElement | null;
|
||||
if (slider) slider.title = Math.round(parseInt(value) / 255 * 100) + '%';
|
||||
}
|
||||
|
||||
@@ -569,13 +569,13 @@ export async function fetchDeviceBrightness(deviceId: any) {
|
||||
if (!resp.ok) return;
|
||||
const data = await resp.json();
|
||||
updateDeviceBrightness(deviceId, data.brightness);
|
||||
const slider = document.querySelector(`[data-device-brightness="${deviceId}"]`) as HTMLInputElement | null;
|
||||
const slider = document.querySelector(`[data-device-brightness="${CSS.escape(deviceId)}"]`) as HTMLInputElement | null;
|
||||
if (slider) {
|
||||
slider.value = data.brightness;
|
||||
slider.title = Math.round(data.brightness / 255 * 100) + '%';
|
||||
slider.disabled = false;
|
||||
}
|
||||
const wrap = document.querySelector(`[data-brightness-wrap="${deviceId}"]`) as HTMLElement | null;
|
||||
const wrap = document.querySelector(`[data-brightness-wrap="${CSS.escape(deviceId)}"]`) as HTMLElement | null;
|
||||
if (wrap) wrap.classList.remove('brightness-loading');
|
||||
} catch (err) {
|
||||
// Silently fail — device may be offline
|
||||
@@ -731,10 +731,10 @@ export async function enrichOpenrgbZoneBadges(deviceId: any, deviceUrl: any) {
|
||||
}
|
||||
|
||||
function _applyZoneCounts(deviceId: any, zones: any, counts: any) {
|
||||
const card = document.querySelector(`[data-device-id="${deviceId}"]`);
|
||||
const card = document.querySelector(`[data-device-id="${CSS.escape(deviceId)}"]`);
|
||||
if (!card) return;
|
||||
for (const zoneName of zones) {
|
||||
const badge = card.querySelector(`[data-zone-name="${zoneName}"]`);
|
||||
const badge = card.querySelector(`[data-zone-name="${CSS.escape(zoneName)}"]`);
|
||||
if (!badge) continue;
|
||||
const ledCount = counts[zoneName.toLowerCase()];
|
||||
if (ledCount != null) {
|
||||
|
||||
Reference in New Issue
Block a user