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:
@@ -87,7 +87,7 @@ export function createPatternTemplateCard(pt: PatternTemplate) {
|
||||
export async function showPatternTemplateEditor(templateId: string | null = null, cloneData: PatternTemplate | null = null): Promise<void> {
|
||||
try {
|
||||
// Load sources for background capture
|
||||
const sources = await streamsCache.fetch().catch(() => []);
|
||||
const sources = await streamsCache.fetch().catch((): any[] => []);
|
||||
|
||||
const bgSelect = document.getElementById('pattern-bg-source') as HTMLSelectElement;
|
||||
bgSelect.innerHTML = '';
|
||||
@@ -116,7 +116,7 @@ export async function showPatternTemplateEditor(templateId: string | null = null
|
||||
setPatternEditorSelectedIdx(-1);
|
||||
setPatternCanvasDragMode(null);
|
||||
|
||||
let _editorTags = [];
|
||||
let _editorTags: string[] = [];
|
||||
|
||||
if (templateId) {
|
||||
const resp = await fetch(`${API_BASE}/pattern-templates/${templateId}`, { headers: getHeaders() });
|
||||
@@ -340,7 +340,7 @@ export function removePatternRect(index: number): void {
|
||||
export function renderPatternCanvas(): void {
|
||||
const canvas = document.getElementById('pattern-canvas') as HTMLCanvasElement;
|
||||
if (!canvas) return;
|
||||
const ctx = canvas.getContext('2d');
|
||||
const ctx = canvas.getContext('2d')!;
|
||||
const w = canvas.width;
|
||||
const h = canvas.height;
|
||||
|
||||
@@ -396,8 +396,8 @@ export function renderPatternCanvas(): void {
|
||||
ctx.strokeRect(rx, ry, rw, rh);
|
||||
|
||||
// Edge highlight
|
||||
let edgeDir = null;
|
||||
if (isDragging && patternCanvasDragMode.startsWith('resize-')) {
|
||||
let edgeDir: string | null = null;
|
||||
if (isDragging && patternCanvasDragMode?.startsWith('resize-')) {
|
||||
edgeDir = patternCanvasDragMode.replace('resize-', '');
|
||||
} else if (isHovered && patternEditorHoverHit && patternEditorHoverHit !== 'move') {
|
||||
edgeDir = patternEditorHoverHit;
|
||||
@@ -586,16 +586,16 @@ function _patternCanvasDragMove(e: MouseEvent | { clientX: number; clientY: numb
|
||||
const mx = (e.clientX - canvasRect.left) * scaleX;
|
||||
const my = (e.clientY - canvasRect.top) * scaleY;
|
||||
|
||||
const dx = (mx - patternCanvasDragStart.mx) / w;
|
||||
const dy = (my - patternCanvasDragStart.my) / h;
|
||||
const orig = patternCanvasDragOrigRect;
|
||||
const dx = (mx - patternCanvasDragStart!.mx!) / w;
|
||||
const dy = (my - patternCanvasDragStart!.my!) / h;
|
||||
const orig = patternCanvasDragOrigRect!;
|
||||
const r = patternEditorRects[patternEditorSelectedIdx];
|
||||
|
||||
if (patternCanvasDragMode === 'move') {
|
||||
r.x = Math.max(0, Math.min(1 - r.width, orig.x + dx));
|
||||
r.y = Math.max(0, Math.min(1 - r.height, orig.y + dy));
|
||||
} else if (patternCanvasDragMode.startsWith('resize-')) {
|
||||
const dir = patternCanvasDragMode.replace('resize-', '');
|
||||
} else if (patternCanvasDragMode?.startsWith('resize-')) {
|
||||
const dir = patternCanvasDragMode!.replace('resize-', '');
|
||||
let nx = orig.x, ny = orig.y, nw = orig.width, nh = orig.height;
|
||||
if (dir.includes('w')) { nx = orig.x + dx; nw = orig.width - dx; }
|
||||
if (dir.includes('e')) { nw = orig.width + dx; }
|
||||
@@ -631,7 +631,7 @@ function _patternCanvasDragEnd(e: MouseEvent): void {
|
||||
const my = (e.clientY - canvasRect.top) * scaleY;
|
||||
let cursor = 'default';
|
||||
let newHoverIdx = -1;
|
||||
let newHoverHit = null;
|
||||
let newHoverHit: string | null = null;
|
||||
if (e.clientX >= canvasRect.left && e.clientX <= canvasRect.right &&
|
||||
e.clientY >= canvasRect.top && e.clientY <= canvasRect.bottom) {
|
||||
for (let i = patternEditorRects.length - 1; i >= 0; i--) {
|
||||
@@ -717,8 +717,8 @@ function _patternCanvasMouseDown(e: MouseEvent | { offsetX?: number; offsetY?: n
|
||||
const rect = canvas.getBoundingClientRect();
|
||||
const scaleX = w / rect.width;
|
||||
const scaleY = h / rect.height;
|
||||
const mx = (e.offsetX !== undefined ? e.offsetX : e.clientX - rect.left) * scaleX;
|
||||
const my = (e.offsetY !== undefined ? e.offsetY : e.clientY - rect.top) * scaleY;
|
||||
const mx = (e.offsetX !== undefined ? e.offsetX : (e.clientX ?? 0) - rect.left) * scaleX;
|
||||
const my = (e.offsetY !== undefined ? e.offsetY : (e.clientY ?? 0) - rect.top) * scaleY;
|
||||
|
||||
// Check delete button on hovered or selected rects first
|
||||
for (const idx of [patternEditorHoveredIdx, patternEditorSelectedIdx]) {
|
||||
@@ -739,7 +739,7 @@ function _patternCanvasMouseDown(e: MouseEvent | { offsetX?: number; offsetY?: n
|
||||
// Test all rects; selected rect takes priority so it stays interactive
|
||||
// even when overlapping with others.
|
||||
const selIdx = patternEditorSelectedIdx;
|
||||
const testOrder = [];
|
||||
const testOrder: number[] = [];
|
||||
if (selIdx >= 0 && selIdx < patternEditorRects.length) testOrder.push(selIdx);
|
||||
for (let i = patternEditorRects.length - 1; i >= 0; i--) {
|
||||
if (i !== selIdx) testOrder.push(i);
|
||||
@@ -795,15 +795,15 @@ function _patternCanvasMouseMove(e: MouseEvent | { offsetX?: number; offsetY?: n
|
||||
const rect = canvas.getBoundingClientRect();
|
||||
const scaleX = w / rect.width;
|
||||
const scaleY = h / rect.height;
|
||||
const mx = (e.offsetX !== undefined ? e.offsetX : e.clientX - rect.left) * scaleX;
|
||||
const my = (e.offsetY !== undefined ? e.offsetY : e.clientY - rect.top) * scaleY;
|
||||
const mx = (e.offsetX !== undefined ? e.offsetX : (e.clientX ?? 0) - rect.left) * scaleX;
|
||||
const my = (e.offsetY !== undefined ? e.offsetY : (e.clientY ?? 0) - rect.top) * scaleY;
|
||||
|
||||
let cursor = 'default';
|
||||
let newHoverIdx = -1;
|
||||
let newHoverHit = null;
|
||||
let newHoverHit: string | null = null;
|
||||
// Selected rect takes priority for hover so edges stay reachable under overlaps
|
||||
const selIdx = patternEditorSelectedIdx;
|
||||
const hoverOrder = [];
|
||||
const hoverOrder: number[] = [];
|
||||
if (selIdx >= 0 && selIdx < patternEditorRects.length) hoverOrder.push(selIdx);
|
||||
for (let i = patternEditorRects.length - 1; i >= 0; i--) {
|
||||
if (i !== selIdx) hoverOrder.push(i);
|
||||
|
||||
Reference in New Issue
Block a user