26 KiB
CLAUDE.md - Developer Documentation
Project Overview
Datacenter Designer is a web-based visual design tool for datacenter infrastructure. It allows users to create, manage, and visualize rack layouts, network devices, and their interconnections in both physical and logical views.
Core Purpose
- Visual rack layout planning (physical positioning)
- Device placement within racks (U1-U42 slots with multi-unit form factors)
- Network connection mapping between devices
- Logical topology view (independent of physical layout)
- Export/import for data portability
Technology Stack
- Frontend: Vanilla JavaScript (ES6 modules), Konva.js (canvas library), ag-Grid (tables), SheetJS (Excel export)
- Backend: Node.js, Express 4.x
- Database: SQLite3
- No build process: Direct ES6 modules, no bundler required
Architecture
High-Level Structure
┌─────────────────────────────────────────────────┐
│ Browser │
│ ┌────────────────────────────────────────────┐ │
│ │ app.js (Main Controller) │ │
│ │ - Orchestrates all managers │ │
│ │ - Handles modals and UI │ │
│ │ - API client │ │
│ │ - View switching (Physical/Logical) │ │
│ └────────────────────────────────────────────┘ │
│ │ │ │ │ │
│ ▼ ▼ ▼ ▼ │
│ ┌────────┐ ┌─────────┐ ┌─────────┐ ┌────────┐ │
│ │ Rack │ │ Device │ │Connect │ │ Table │ │
│ │Manager │ │Manager │ │Manager │ │Manager │ │
│ └────────┘ └─────────┘ └─────────┘ └────────┘ │
│ │ │ │ │ │
│ └──────────┴───────────┴──────────┘ │
│ │ │
│ Konva.js Canvas │
└────────────────────│──────────────────────────────┘
│ HTTP/REST API
┌────────────────────▼──────────────────────────────┐
│ Server │
│ ┌──────────────────────────────────────────────┐│
│ │ server.js (Express Router) ││
│ │ - RESTful endpoints ││
│ │ - Request/response handling ││
│ └──────────────────────────────────────────────┘│
│ │ │
│ ┌──────────────────▼──────────────────────────┐ │
│ │ db.js (Database Layer) │ │
│ │ - Promise-wrapped SQLite operations │ │
│ │ - Schema management │ │
│ └──────────────────────────────────────────────┘ │
│ │ │
│ SQLite Database File │
└───────────────────────────────────────────────────┘
File Organization
datacenter-designer/
├── server/
│ ├── server.js (276 LOC) - Express routes
│ └── db.js (547 LOC) - Database operations
├── public/
│ ├── index.html (193 LOC) - Main HTML structure
│ ├── css/
│ │ └── style.css (741 LOC) - All styles
│ └── js/
│ ├── app.js (1719 LOC) - Main controller
│ ├── rack-manager.js (488 LOC) - Rack rendering/interaction
│ ├── device-manager.js (513 LOC) - Device rendering/interaction
│ ├── connection-manager.js (901 LOC) - Connection lines/waypoints
│ └── table-manager.js (792 LOC) - ag-Grid table views
├── database/
│ └── datacenter.db (Created at runtime)
├── package.json
└── README.md
Total: ~5,236 lines of code
Critical Review
✅ Strengths
- Clean Separation of Concerns: Manager pattern isolates rack, device, connection, and table logic
- Simple Tech Stack: No build process, direct ES6 modules, minimal dependencies
- RESTful API: Clear HTTP API with predictable endpoints
- SQLite Persistence: Appropriate for local/desktop deployment
- Konva.js Integration: Good choice for canvas-based diagramming
- Dual View System: Physical (rack-based) and logical (topology) views
❌ Issues & Anti-Patterns
1. Monolithic app.js (1719 lines)
- Violation: Single Responsibility Principle
- Contains: API client, modal management, zoom/pan, view switching, event coordination, export/import
- Should be: Split into smaller modules (API client, ModalManager, StateManager, UIController)
2. Repetitive Code (DRY Violations)
// Repeated everywhere in server.js:
try {
// ... logic
} catch (err) {
res.status(500).json({ error: err.message });
}
// Repeated modal patterns in app.js
modal.classList.remove('hidden');
// ...setup
modal.classList.add('hidden');
3. Database Layer Issues
- Callback-based sqlite3: Manually wrapping every query in Promises
- No migration system: Schema changes via ALTER TABLE with error swallowing (lines 106-140 in db.js)
- Verbose: Every CRUD operation is 10-15 lines of boilerplate
- Better alternative:
better-sqlite3(synchronous, simpler, faster)
4. Hard-Coded Magic Numbers
// Scattered throughout codebase:
const maxSlots = 42; // Rack slots
const rackWidth = 520; // Rack dimensions
const rackHeight = 1510;
const gridSize = 600; // Grid spacing
const deviceHeight = 32;
const deviceSpacing = 2;
const topMargin = 10;
Should be: Centralized in config.js with semantic names
5. API Client Design
class API {
// One method per endpoint - not scalable
getRacks() { return this.request('/api/racks?projectId=...'); }
createRack(...) { return this.request(...); }
updateRackPosition(...) { return this.request(...); }
// ... 30+ methods
}
Should be: Generic resource methods or use a library
6. CSS Organization
- 741 lines in one file: No modularity
- Hard-coded colors:
#4A90E2,#f5f5f5, etc. repeated - No CSS variables: Should use
--primary-color,--bg-color, etc. - No responsiveness: No media queries, fixed dimensions
- No dark mode support: Hard-coded light theme only
7. State Management
- Scattered state: Each manager holds its own state in Maps
- DOM as state: Using localStorage without versioning
- No synchronization: Manual
canvas-data-changedevents - No undo/redo: No state history
8. Input Validation
- Server: Minimal validation (trusts client)
- Client: No form validation, uses
prompt()for input - No schema validation: No Zod, Joi, or similar
9. Error Handling
// Current:
catch (err) {
console.error('Failed:', err);
alert('Failed: ' + err.message);
}
Should be: Toast notifications, user-friendly messages, error boundaries
10. Export/Import
- No versioning: Export format has version field but isn't checked properly
- Monolithic: One giant JSON blob
- No incremental import: All-or-nothing
⚠️ Over-Engineering
- View-Specific Waypoints:
waypoints_physicalandwaypoints_logicalstored separately- Better: Store once, transform with view matrix
- Separate
rack_unitscolumn: Could be derived fromdevice_type - Multiple modal patterns: Overlapping modal handling code
⚠️ Under-Engineering
- No undo/redo: Critical for a design tool
- No keyboard shortcuts: Only
EscandDelsupported - No search/filter: As data grows, navigation becomes difficult
- No loading states: UI freezes during operations
- No optimistic updates: Waits for server before updating UI
- No batch operations: Can't select multiple racks/devices
- No auto-save: Only manual saves via export
Better Technologies / Alternatives
Database
Current: sqlite3 (callback-based)
Better: better-sqlite3
- Synchronous API (simpler code)
- 2-3x faster
- No callback hell
// Current:
return new Promise((resolve, reject) => {
this.db.get('SELECT * FROM racks WHERE id = ?', [id], (err, row) => {
if (err) reject(err);
else resolve(row);
});
});
// With better-sqlite3:
return db.prepare('SELECT * FROM racks WHERE id = ?').get(id);
State Management
Current: Scattered Maps in each manager Better Options:
- Zustand (100 lines, minimal API)
- Valtio (proxy-based reactivity)
- Nanostores (atomic stores)
API Client
Current: Custom 30+ method API class Better: ky or axios with interceptors
// Generic resource client:
const api = {
get: (url) => ky.get(url).json(),
post: (url, data) => ky.post(url, { json: data }).json(),
// ... + error handling in interceptors
};
CSS Organization
Current: 741-line monolithic file Better: Split by concern + CSS variables
/* config.css */
:root {
--primary-color: #4A90E2;
--bg-color: #f5f5f5;
--rack-width: 520px;
--rack-height: 1510px;
}
/* components/rack.css */
/* components/device.css */
/* layout.css */
/* theme.css */
Configuration
Current: Hard-coded everywhere
Better: config.js
export const RACK_CONFIG = {
WIDTH: 520,
HEIGHT: 1510,
SLOTS: 42,
MARGIN: { top: 10, right: 10, bottom: 10, left: 10 }
};
export const GRID_CONFIG = {
HORIZONTAL: 600,
VERTICAL: 1610
};
Validation
Current: None Better: Zod for schema validation
const RackSchema = z.object({
name: z.string().min(1).max(50),
x: z.number().finite(),
y: z.number().finite()
});
Recommended Refactoring Strategy
Option A: In-Place Refactoring (Direct Edit)
Pros:
- Keep git history
- Incremental changes
- Can test at each step
Cons:
- Risk of breaking working features
- Harder to compare old vs new
- No fallback
Option B: Release Candidate Approach (Recommended)
Pros:
- Clean slate while keeping original
- Can compare side-by-side
- Easy to revert
- Clear migration path
Cons:
- Duplicated files temporarily
- Need to sync any new features
Proposed Structure:
datacenter-designer/
├── rc/ # New refactored version
│ ├── server/
│ │ ├── config.js # NEW: Configuration
│ │ ├── db.js # REFACTORED: better-sqlite3
│ │ ├── routes/ # NEW: Split routes
│ │ │ ├── projects.js
│ │ │ ├── racks.js
│ │ │ ├── devices.js
│ │ │ └── connections.js
│ │ └── server.js # SIMPLIFIED: Just setup
│ ├── public/
│ │ ├── index.html # UPDATED: More semantic
│ │ ├── css/
│ │ │ ├── config.css # NEW: CSS variables
│ │ │ ├── layout.css
│ │ │ ├── components.css
│ │ │ └── theme.css
│ │ └── js/
│ │ ├── config.js # NEW: Constants
│ │ ├── lib/
│ │ │ ├── api.js # REFACTORED: Simplified
│ │ │ ├── state.js # NEW: State manager
│ │ │ └── ui.js # NEW: UI utilities
│ │ ├── managers/
│ │ │ ├── rack-manager.js
│ │ │ ├── device-manager.js
│ │ │ ├── connection-manager.js
│ │ │ └── table-manager.js
│ │ └── app.js # SLIMMED: Orchestrator only
│ └── database/
│ └── .gitkeep
├── server/ # OLD: Keep during transition
├── public/ # OLD: Keep during transition
├── .gitignore
├── CLAUDE.md # This file
├── README.md
└── package.json
Refactoring Checklist
Phase 1: Foundation (Day 1-2)
- Create
rc/directory structure - Setup
.gitignoreproperly - Create
rc/server/config.jswith all constants - Create
rc/public/js/config.jswith frontend constants - Port database to
better-sqlite3 - Create migration system
- Split CSS into modules with CSS variables
- Make layout responsive (media queries)
Phase 2: Backend (Day 3)
- Split routes into separate files
- Add input validation (Zod or similar)
- Implement error middleware
- Add request logging
- Create API documentation (OpenAPI/Swagger)
Phase 3: Frontend Core (Day 4-5)
- Extract API client to
lib/api.js - Extract state management to
lib/state.js - Extract UI utilities to
lib/ui.js(modal manager, toast, etc.) - Slim down
app.jsto orchestration only - Refactor managers to use new state system
Phase 4: Features & UX (Day 6-7)
- Add toast notifications (replace
alert()) - Add loading states
- Add keyboard shortcuts
- Add undo/redo system
- Add search/filter
- Add batch operations
- Improve error messages
Phase 5: Testing & Documentation (Day 8)
- Add unit tests for backend
- Add integration tests
- Update README.md
- Update CLAUDE.md
- Add inline JSDoc comments
- Performance testing
Phase 6: Migration (Day 9-10)
- Test thoroughly
- Migrate data from old DB if needed
- Move
rc/to root - Archive old code to
old/ - Update package.json scripts
- Final testing
Key Principles to Follow
KISS (Keep It Simple, Stupid)
- Avoid abstractions unless proven necessary
- Prefer composition over inheritance
- Use vanilla JS unless library provides clear value
DRY (Don't Repeat Yourself)
- Extract repeated patterns (error handling, modal management)
- Use constants instead of magic numbers
- Create reusable utility functions
SOLID Principles
- S: Single Responsibility - Each module does one thing
- O: Open/Closed - Extend without modifying
- L: Liskov Substitution - Subtypes must be substitutable
- I: Interface Segregation - Many specific interfaces > one general
- D: Dependency Inversion - Depend on abstractions
WORM (Write Once, Read Many)
- Optimize for read performance
- Use indices on frequently queried columns
- Cache frequently accessed data
- Minimize database writes
Additional Principles
- Fail Fast: Validate early, throw errors quickly
- Convention over Configuration: Sensible defaults
- Progressive Enhancement: Works without JS, enhanced with it
- Accessibility: Keyboard navigation, ARIA labels, screen reader support
Responsive Design Strategy
Breakpoints
/* Mobile: < 768px */
@media (max-width: 767px) {
/* Stack toolbar items, hide non-essential controls */
}
/* Tablet: 768px - 1024px */
@media (min-width: 768px) and (max-width: 1024px) {
/* Optimize for touch, larger buttons */
}
/* Desktop: > 1024px */
@media (min-width: 1025px) {
/* Full feature set */
}
Layout Strategies
- Toolbar: Collapse to hamburger menu on mobile
- Canvas: Always full viewport minus toolbar
- Tables: Horizontal scroll on mobile, fixed on desktop
- Modals: Full-screen on mobile, centered on desktop
- Context Menus: Bottom sheet on mobile, popup on desktop
Touch Support
- Increase hit targets to 44x44px minimum
- Add touch gestures (pinch-zoom, two-finger pan)
- Show hover states on touch as active states
- Use native select/input elements on mobile
Performance Considerations
Canvas Rendering
- Use
batchDraw()instead ofdraw()for multiple updates - Implement viewport culling (don't render off-screen items)
- Use
cache()for static shapes - Debounce pan/zoom updates
Database
- Add indices on foreign keys
- Use transactions for bulk operations
- Implement connection pooling (if moving to client-server)
- Lazy-load connections (don't load all at startup)
Network
- Implement request debouncing for auto-save
- Use HTTP caching headers
- Compress API responses (gzip)
- Implement pagination for large datasets
Security Considerations
SQL Injection
- ✅ Already using parameterized queries
- Add input sanitization layer
- Validate all inputs on server
XSS (Cross-Site Scripting)
- Sanitize user input before displaying
- Use
textContentinstead ofinnerHTMLwhere possible - Add Content Security Policy headers
CSRF (Cross-Site Request Forgery)
- Add CSRF tokens for state-changing operations
- Use SameSite cookies
File Upload
- If adding import from file:
- Validate file size
- Validate file type
- Scan for malicious content
.gitignore Strategy
To avoid pushing sensitive or generated files:
# Dependencies
node_modules/
package-lock.json # Optional: some include, some exclude
# Database (user data)
database/*.db
database/*.db-shm
database/*.db-wal
*.db
*.db-shm
*.db-wal
# Logs
logs/
*.log
npm-debug.log*
# Environment variables
.env
.env.local
.env.*.local
# IDE
.vscode/
.idea/
*.swp
*.swo
*~
.DS_Store
# Build outputs (if added later)
dist/
build/
.cache/
# Temporary files
tmp/
temp/
*.tmp
# OS files
Thumbs.db
.DS_Store
# Claude-specific (optional)
.claude/
Important: If database contains sample data for demonstrations, create a separate database/sample.db and explicitly include it:
# Ignore user databases
database/*.db
# But include sample
!database/sample.db
Testing Strategy
Unit Tests
- Test database operations in isolation
- Test utility functions
- Test state management
- Use: Vitest or Jest
Integration Tests
- Test API endpoints
- Test full CRUD workflows
- Use: Supertest + Vitest
E2E Tests
- Test critical user journeys
- Test cross-browser compatibility
- Use: Playwright or Cypress
Test Structure
// Example: tests/db.test.js
import { describe, it, expect, beforeEach } from 'vitest';
import db from '../server/db.js';
describe('Database - Racks', () => {
beforeEach(() => {
// Reset database to known state
});
it('should create a rack', () => {
const rack = db.createRack(1, 'RACK01', 0, 0);
expect(rack.id).toBeDefined();
expect(rack.name).toBe('RACK01');
});
it('should not create duplicate rack names in same project', () => {
db.createRack(1, 'RACK01', 0, 0);
expect(() => db.createRack(1, 'RACK01', 100, 0)).toThrow();
});
});
Future Enhancements Roadmap
Phase 1: Core UX Improvements
- Undo/redo system
- Enhanced keyboard shortcuts
- Toast notifications (replace browser alerts)
- Loading states
- Search and filter functionality
- Batch operations (select multiple items)
- Auto-save functionality
Phase 2: Multi-User Support
- User management system
- Authentication and authorization
- OIDC-compatible external SSO integration
- Project sharing between users
- Role-based access control (view/edit/admin)
- User profile management
Phase 3: Collaboration
- Real-time collaborative editing (WebSocket)
- Concurrent access management
- User presence indicators
- Change notifications
- Conflict resolution
- Version history / snapshots
- Activity audit logging
Phase 4: Advanced Features
- Custom device types (user-defined)
- Cable labeling and management
- VLAN visualization
- IP address management
- Documentation generation (diagrams, BOMs)
- Import from other formats (Visio, Lucidchart)
- PostgreSQL/MySQL support
Phase 5: Platform Enhancements
- Dark mode
- Mobile/tablet responsive design
- 3D rack visualization
- Cable routing visualization
- Enhanced export formats
- API for 3rd party integrations
- Backup/restore automation
Common Gotchas & Pitfalls
Konva-Specific
- Coordinate Systems: Konva uses top-left origin, racks use bottom-up slot numbering (U1 at bottom)
- Event Bubbling: Events on canvas can propagate unexpectedly - use
e.cancelBubble = true - Memory Leaks: Always destroy unused layers/shapes
- Performance: Too many shapes? Use
virtualizerpattern
SQLite-Specific
- Foreign Keys: Must enable explicitly with
PRAGMA foreign_keys = ON - Concurrency: Only one write at a time - queue writes or use WAL mode
- Migrations: No built-in system - roll your own or use library
- Type System: Dynamic typing can surprise you (stores as INTEGER, returns as Number)
CSS/Canvas Interaction
- Canvas Size: Must set both CSS size and Konva stage size
- DPI Scaling: High-DPI displays may need pixel ratio adjustment
- Touch Events: Handle both mouse and touch events separately
Contributing Guidelines
Code Style
- Indentation: 2 spaces
- Quotes: Single quotes for strings
- Semicolons: Required
- Naming: camelCase for variables/functions, PascalCase for classes
- Line Length: 100 characters max
- Comments: JSDoc for public APIs, inline for complex logic
Commit Messages
Follow Conventional Commits:
feat: add undo/redo system
fix: correct device positioning in logical view
docs: update CLAUDE.md with testing strategy
refactor: extract API client to separate file
test: add unit tests for database operations
chore: update dependencies
Pull Request Process
- Create feature branch from
main - Make changes with tests
- Update documentation
- Run linter and tests
- Submit PR with description
- Address review comments
- Merge when approved
Debugging Tips
Backend Debugging
# Enable SQLite query logging
DEBUG=sqlite3 npm start
# Enable Express debug logging
DEBUG=express:* npm start
# Enable all debug logging
DEBUG=* npm start
Frontend Debugging
// Enable Konva debugging
Konva.showWarnings = true;
// Log all API calls
api.request = new Proxy(api.request, {
apply(target, thisArg, args) {
console.log('[API]', args[0], args[1]);
return target.apply(thisArg, args);
}
});
Common Issues
- Canvas not updating: Call
.batchDraw()on layer - Events not firing: Check if shape is
listening(true) - Drag not working: Check if shape is
draggable(true)and parent layer is listening - Database locked: Close all connections, check for transactions
- CORS errors: Ensure frontend and backend are on same origin or CORS is enabled
Glossary
- Rack: Physical equipment cabinet with 42U slots
- U / Rack Unit: Standard height measurement (1U = 1.75 inches)
- Slot: Position within a rack (U1 at bottom, U42 at top)
- Device: Network equipment (switch, router, firewall, etc.)
- Form Factor: Device height in rack units (1U, 2U, 4U, etc.)
- Connection: Network link between two device ports
- Waypoint: Intermediate point in a connection line for routing
- Physical View: Rack-based layout showing physical positioning
- Logical View: Topology view ignoring physical constraints
- Project: Isolated workspace containing racks, devices, connections
- Canvas: Konva.js drawing surface
- Stage: Top-level Konva container
- Layer: Konva organizational unit (rack layer, device layer, connection layer)
- Shape: Individual Konva element (rectangle, text, line, etc.)
Links & Resources
Dependencies
- Konva.js Documentation
- ag-Grid Documentation
- SheetJS Documentation
- SQLite Documentation
- better-sqlite3
Design Patterns
Best Practices
Revision History
| Version | Date | Author | Changes |
|---|---|---|---|
| 0.1.0 | 2025-10-26 | Claude | Initial release - comprehensive documentation |
License
MIT License - See LICENSE file for details