16 KiB
Deathwatch Roller - Development & Operations Guide
Complete documentation for building, testing, deploying, and maintaining the Deathwatch Roller application. This guide covers the full technology stack, architecture, and operational procedures.
Project Overview
Deathwatch Roller is a full-stack web application for managing tabletop RPG gameplay. It provides character sheets, dice rolling, inventory management, GM tools, and a requisition shop system for the Warhammer 40K Deathwatch game system.
Technology Stack
Frontend
- Framework: React 18.2.0
- Styling: Tailwind CSS
- Testing: Jest + React Testing Library
- Build Tool: create-react-app (react-scripts)
- State Management: React hooks (useState, useCallback, useEffect)
- HTTP Client: Axios
Backend
- Runtime: Node.js
- Framework: Express.js 5.1.0
- Database: MariaDB
- Security: bcrypt, CORS, custom session validation
- File Processing: PDF-parse (for importing game data)
DevOps
- Process Management: PM2 (backend server)
- Frontend Serving: serve (static file server on port 3000)
- Backend API: Running on port 5000
- Build Output: Production builds in
/builddirectory
Architecture Overview
┌─────────────────────────────────────────────────────────────┐
│ CLIENT (React) │
│ - Player Tab (character sheets, inventory) │
│ - Dice Roller (d100 rolls with modifiers) │
│ - Requisition Shop (purchase equipment with RP) │
│ - Bestiary (enemy database and stats) │
│ - Rules (searchable game rules reference) │
│ - GM Kit (enemy generation, bulk operations) │
│ - Player Management (admin panel for GMs) │
│ Port: 3000 (via serve or npm start) │
└────────────┬────────────────────────────────────────────────┘
│
HTTP/CORS
│
┌────────────▼────────────────────────────────────────────────┐
│ BACKEND (Express) │
│ API Routes: │
│ - /api/players - Player CRUD & character data │
│ - /api/sessions - Session validation & authentication │
│ - /api/shop - Requisition shop purchases │
│ - /api/rules - Game rules database access │
│ - /api/weapons - Weapon stats & properties │
│ - /api/bestiary - Enemy database access │
│ - /api/gmkit - GM tools & bulk operations │
│ Port: 5000 │
└────────────┬────────────────────────────────────────────────┘
│
SQL Queries
│
┌────────────▼────────────────────────────────────────────────┐
│ MariaDB Database │
│ Tables: │
│ - players (character data, RP, XP, renown) │
│ - sessions (authentication) │
│ - weapons (weapon stats & costs) │
│ - rules (game rules reference) │
│ - bestiary (enemy data) │
└─────────────────────────────────────────────────────────────┘
Project Structure
dwroller/
├── src/
│ ├── components/ # React UI components
│ │ ├── PlayerManagement.jsx # GM admin panel
│ │ ├── PlayerTab.jsx # Player character sheet
│ │ ├── DeathwatchRoller.jsx # Dice rolling interface
│ │ ├── RequisitionShop.jsx # Equipment shop
│ │ ├── BestiaryTab.jsx # Enemy database
│ │ ├── RulesTab.jsx # Rules reference
│ │ ├── GMKit.jsx # GM tools
│ │ ├── XPBar.jsx # Experience progress bar
│ │ └── GMKit_old.jsx # Legacy (unused)
│ ├── utils/
│ │ └── logger.js # Logging utility
│ ├── tests/
│ │ ├── bestiaryTab.test.js
│ │ ├── login.test.js
│ │ ├── playerManagement.test.js
│ │ └── requisitionShop.test.js
│ ├── App.js # Main app with routing
│ ├── App.css # App styling
│ ├── index.js # React entry point
│ ├── index.css # Global Tailwind styles
│ └── index.html # HTML template
├── database/
│ ├── server.js # Express API server
│ ├── mariadb.js # Database connection
│ ├── sessionModel.js # Session management
│ ├── pm2.config.js # PM2 configuration
│ ├── requireSession.js # Auth middleware
│ ├── shop-helpers.js # Shop business logic
│ ├── switch-db.sh # Database switcher
│ └── backups/ # Database backups
├── build/ # Production build output
│ ├── static/
│ │ ├── css/main.*.css # Compiled Tailwind
│ │ └── js/ # Bundled JavaScript
│ └── index.html # Served HTML
├── backup-scripts/ # Utility scripts
│ ├── migrate-to-sqlite.js
│ ├── sync-skills-csv-to-db.js
│ └── ...
├── package.json # Dependencies & scripts
├── tailwind.config.js # Tailwind configuration
├── jest.config.js # Jest test configuration
├── eslint.config.js # ESLint configuration
└── README.md # Project readme
Build Process
Local Development
# Install dependencies
npm install
# Start dev server with hot reload
npm start
# or
npm run dev
Build Steps
1. Run Tests
npm run test:unit
# Runs all unit tests using Jest
2. Build for Production
npm run build
# Executes: npm run test:unit && react-scripts build
# Creates optimized production bundle in /build
3. Verify Build Output
# Check that build directory was created with static files
ls -la build/static/css/
ls -la build/static/js/
# Verify CSS size and integrity
gzip -c build/static/css/main.*.css | wc -c
# Expected: ~6-7 KB gzipped
Build Configuration
- Source Maps: Generated for debugging
- CSS Minification: Automatic via react-scripts
- JS Minification: Webpack tree-shaking + uglification
- Public Path: Set to
/for root-level deployment
Alternative Build Commands
# Fast build without tests (use carefully)
npm run build:fast
# Build with integration tests
npm run build:with-integration
# Test -> Build -> Test cycle
npm run test-build-test
Deployment Process
1. Build for Production
cd /home/alex/git/dwroller
npm run build
# Output: /build directory ready for deployment
2. Start Frontend Server
# Serve the production build on port 3000
npx serve -s build -l 3000 &
# Or with PM2 (optional)
pm2 start "npx serve -s build -l 3000" --name dwroller-frontend
pm2 save
3. Ensure Backend is Running
# Start backend (if not already running)
pm2 start database/server.js --name deathwatch-server
# Verify it's running
pm2 list
pm2 logs deathwatch-server
4. Verify The Live Site
After a production build and PM2 reload, open https://dwroller.alw.dk and
check the exact flow you changed on the deployed site. A successful local build
or PM2 reload is not enough by itself; live-site verification is required for
changes that affect production behavior.
5. Verify Deployment
# Test frontend accessibility
curl http://localhost:3000
# Test API connectivity
curl http://localhost:5000/api/players
# Check both are running
ps aux | grep -E "serve|node"
Testing
Unit Tests
npm run test:unit
# Runs Jest with --runInBand to avoid conflicts
Current Test Status:
- ✅ 13 tests passing
- ⚠️ 12 tests failing (pre-existing, architectural issues)
- Tests cover: login, requisition shop, bestiary, player management
Running Specific Tests
# Run a specific test file
npm test -- src/tests/login.test.js
# Run tests matching a pattern
npm test -- --testNamePattern="PlayerManagement"
# Watch mode (automatic re-run on file changes)
npm test -- --watch
Test Coverage
npm test -- --coverage
# Shows coverage report for all tested files
Troubleshooting & Maintenance
Frontend Not Updating
Problem: Changes to source files not appearing in browser Solution:
# Full rebuild
rm -rf build
npm run build
# Kill old serve process and restart
pkill -9 -f "serve.*build"
npx serve -s build -l 3000 &
# Hard refresh browser: Ctrl+Shift+R (Windows/Linux) or Cmd+Shift+R (Mac)
# Clear browser cache manually in DevTools
Database Connection Issues
Problem: "Failed to fetch players" error Solution:
# Check if backend is running
pm2 list | grep deathwatch
# View backend logs
pm2 logs deathwatch-server --lines 100
# Check database connection
curl http://localhost:5000/api/players
# Verify MariaDB is running
systemctl status mariadb
# or
mysql -u root -p -e "SELECT 1;"
Port Already in Use
Problem: "EADDRINUSE: address already in use :::3000" Solution:
# Find process using port 3000
lsof -i :3000
# or
netstat -tulpn | grep 3000
# Kill the process
kill -9 <PID>
# Or use a different port
npx serve -s build -l 3001
API CORS Errors
Problem: "Access to XMLHttpRequest blocked by CORS policy" Solution: CORS is already configured in backend (database/server.js)
- Proxy is configured in package.json:
"proxy": "http://localhost:5000" - Requests to
/api/*are automatically routed to backend - If issues persist, check backend logs and CORS configuration
Performance Optimization
Frontend
- Code Splitting: React automatically chunks large components
- CSS: Tailwind purges unused classes in production
- JS: Webpack tree-shaking removes dead code
- Assets: Images optimized by build process
Backend
- Query Caching: Rules and bestiary data cached in memory
- Session Validation: Simple token-based (can be improved)
- Database Indexing: Ensure proper indexes on player ID, session ID
Monitoring
# Check memory usage
pm2 list
# Monitor in real-time
pm2 monit
# View detailed logs
pm2 logs
# Check response times
curl -w "@curl-format.txt" http://localhost:5000/api/players
Security Considerations
Authentication
- ✅ Session-based with session ID validation
- ✅ bcrypt password hashing
- ⚠️ Default password (1234) for testing - CHANGE IN PRODUCTION
- ⚠️ Simple token format - consider JWT
Authorization
- ✅ GM role check for Player Management
- ✅ Session validation on API routes
- ⚠️ No rate limiting implemented
- ⚠️ No input validation/sanitization
HTTPS
- ⚠️ Not enforced (add in production)
- Use reverse proxy (nginx) or cloud provider SSL
Recommended Improvements
- Remove hardcoded test password
- Implement JWT tokens instead of session IDs
- Add rate limiting middleware
- Add input validation/sanitization
- Enable HTTPS/SSL
- Implement CSRF protection
- Add request logging and monitoring
Database
Connection
- Host: localhost (configured in database/mariadb.js)
- User: configurable (default: root)
- Database: dwroller (or deathwatch)
- Driver: mysql2
Main Tables
-- Players table
CREATE TABLE players (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(255) UNIQUE NOT NULL,
passwordHash VARCHAR(255) NOT NULL,
rp INT DEFAULT 0,
xp INT DEFAULT 0,
renown VARCHAR(50) DEFAULT 'None',
tabInfo JSON,
createdAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- Sessions table
CREATE TABLE sessions (
id VARCHAR(255) PRIMARY KEY,
playerName VARCHAR(255) NOT NULL,
createdAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
expiresAt TIMESTAMP
);
-- Weapons, Rules, Bestiary tables
-- (See database/mariadb.js for full schema)
Backup & Restore
# Backup database
mysqldump -u root -p dwroller > backup.sql
# Restore database
mysql -u root -p dwroller < backup.sql
# Verify backup integrity
npm run backup-scripts/repair-skills.js
Development Workflow
Making Changes
- Create feature branch:
git checkout -b feature/my-feature - Make code changes
- Test locally:
npm startand manual testing - Run tests:
npm run test:unit - Build for production:
npm run build - Commit and push
- Deploy: Follow deployment process above
Code Style
- ESLint configuration in eslintConfig (package.json)
- Tailwind CSS for styling (not inline styles)
- React hooks for state management (not class components)
- Functional components preferred
Git Workflow
# View changes
git status
git diff
# Commit changes
git add .
git commit -m "Fix opacity issues in UI"
# Push to branch
git push origin clean-main
# Check current branch
git branch -a
Environment Variables (if needed)
Create .env file in project root:
REACT_APP_API_URL=http://localhost:5000
REACT_APP_DEBUG=false
Access in React:
const apiUrl = process.env.REACT_APP_API_URL;
Monitoring & Logs
Frontend Logs
# Browser console (DevTools F12)
- Network tab: Check API calls and response times
- Console tab: Check for JavaScript errors
- Application tab: Inspect stored data (localStorage)
Backend Logs
# Real-time logs
pm2 logs deathwatch-server
# Last 100 lines
pm2 logs deathwatch-server --lines 100
# Specific error log
pm2 logs deathwatch-server --err
# Stream logs to file
pm2 logs deathwatch-server > logs.txt
Common Workflows
Deploy a Hotfix
git checkout clean-main
git pull origin clean-main
# Make fixes to source files
npm run build
pkill -9 -f "serve.*build"
npx serve -s build -l 3000 &
# Test in browser
Rollback to Previous Build
# Keep previous build in backup
mv build build.backup
git checkout previous-commit
npm run build
npx serve -s build -l 3000 &
Scale Frontend to Multiple Instances
# Instead of single serve process, use PM2 with clustering
pm2 start "npx serve -s build -l 3000" --name dwroller-frontend-1
pm2 start "npx serve -s build -l 3001" --name dwroller-frontend-2
pm2 start "npx serve -s build -l 3002" --name dwroller-frontend-3
# Load balance with nginx
# (Configure nginx upstream to round-robin across ports)
References & Documentation
Official Docs
Project Files
- Backend routes:
database/server.js - Database connection:
database/mariadb.js - Authentication:
database/sessionModel.js - Middleware:
database/requireSession.js
Last Updated: December 11, 2025 Branch: clean-main Status: ✅ Production Ready