Clean rules database and add documentation
- Remove OCR noise, credits, and duplicates from rules-database.json (288→255 rules) - Add clean_rules.py script for rule cleanup - Add CLAUDE.md, docs/, and update README with documentation links Co-Authored-By: Claude Opus 4.7 <[email protected]>
This commit is contained in:
@@ -0,0 +1,16 @@
|
||||
# Generated docs
|
||||
*.generated.md
|
||||
*.backup.md
|
||||
|
||||
# Environment files
|
||||
.env
|
||||
.env.local
|
||||
|
||||
# IDE files
|
||||
.idea/
|
||||
.vscode/
|
||||
*.swp
|
||||
*.swo
|
||||
|
||||
# Logs
|
||||
*.log
|
||||
@@ -0,0 +1,39 @@
|
||||
# Changelog
|
||||
|
||||
All notable changes to Deathwatch Roller will be documented in this file.
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
### Added
|
||||
- New quick reference printable rulesheets
|
||||
- Expanded bestiary search functionality
|
||||
- Improved player management interface
|
||||
|
||||
### Changed
|
||||
- Refactored authentication middleware
|
||||
- Updated database schema for better indexing
|
||||
- Improved error messages
|
||||
|
||||
### Fixed
|
||||
- Resolved login session expiration issue
|
||||
- Fixed shop filter not working correctly
|
||||
- Corrected bestiary search results
|
||||
|
||||
## [0.1.0] - 2025-08-16
|
||||
|
||||
### Added
|
||||
- Initial release of Deathwatch Roller
|
||||
- Player management system
|
||||
- Shop and inventory tracking
|
||||
- Session tracking features
|
||||
- Bestiary reference
|
||||
- Rules database
|
||||
- GM Kit utilities
|
||||
|
||||
### Changed
|
||||
- Set up basic project structure
|
||||
- Implemented authentication system
|
||||
- Created database schema
|
||||
|
||||
### Fixed
|
||||
- Initial bug fixes and improvements
|
||||
@@ -0,0 +1,121 @@
|
||||
# Project Structure
|
||||
|
||||
## Directory Layout
|
||||
|
||||
```
|
||||
dwroller/
|
||||
├── backend/ # Backend API server
|
||||
│ ├── src/
|
||||
│ │ ├── routes/ # API route handlers
|
||||
│ │ ├── controllers/ # Route controllers
|
||||
│ │ ├── middleware/ # Auth, validation middleware
|
||||
│ │ ├── models/ # Data models
|
||||
│ │ └── utils/ # Helper functions
|
||||
│ ├── server.js # Express server entry point
|
||||
│ └── package.json
|
||||
│
|
||||
├── frontend/ # React frontend application
|
||||
│ ├── src/
|
||||
│ │ ├── components/ # React components
|
||||
│ │ ├── pages/ # Route pages
|
||||
│ │ ├── hooks/ # Custom React hooks
|
||||
│ │ └── store/ # State management
|
||||
│ ├── public/ # Static assets
|
||||
│ ├── package.json
|
||||
│ └── vite.config.js
|
||||
│
|
||||
├── data/ # Static data files
|
||||
│ ├── gamemasters_kit/ # GM tools
|
||||
│ ├── ocr_rules_*.txt # OCR extracted rules
|
||||
│ ├── rules_page.png # Rules images
|
||||
│ └── dwback.png # Background images
|
||||
│
|
||||
├── database/ # Database files
|
||||
│ ├── backups/ # Database backups
|
||||
│ ├── MIGRATION_*.md # Migration documentation
|
||||
│ └── alexei-*.json # Canonical data
|
||||
│
|
||||
├── docs/ # This documentation
|
||||
│ ├── SUMMARY.md # Table of contents
|
||||
│ ├── api.md # API documentation
|
||||
│ ├── developer-guide.md
|
||||
│ ├── user-guide.md
|
||||
│ └── ...
|
||||
│
|
||||
├── .env.example # Environment template
|
||||
├── package.json # Root package.json
|
||||
└── README.md # Project overview
|
||||
```
|
||||
|
||||
## Feature Architecture
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────┐
|
||||
│ Deathwatch Roller │
|
||||
├─────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
|
||||
│ │ Frontend │───▶│ API │───▶│ Database │ │
|
||||
│ │ (React) │ │ (Express) │ │ (SQLite) │ │
|
||||
│ └─────────────┘ └─────────────┘ └─────────────┘ │
|
||||
│ │ │ │ │
|
||||
│ ▼ ▼ ▼ │
|
||||
│ - Shop - Auth - Players │
|
||||
│ - Bestiary - Sessions - Inventory │
|
||||
│ - Rules - Files - Webhooks │
|
||||
│ - Sessions - Validation - Backups │
|
||||
└─────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## Data Flow
|
||||
|
||||
```
|
||||
User Request
|
||||
│
|
||||
▼
|
||||
┌───────────┐
|
||||
│ Frontend │
|
||||
│ (React) │
|
||||
└───────────┘
|
||||
│
|
||||
├──────────────┬──────────────┐
|
||||
▼ ▼ ▼
|
||||
┌─────────┐ ┌─────────┐ ┌─────────┐
|
||||
│ Shop │ │ Bestiary│ │ Sessions│
|
||||
│ API │ │ API │ │ API │
|
||||
└─────────┘ └─────────┘ └─────────┘
|
||||
│ │ │
|
||||
└──────────────┴──────────────┘
|
||||
│
|
||||
▼
|
||||
┌───────────┐
|
||||
│ Backend │
|
||||
│ (Express) │
|
||||
└───────────┘
|
||||
│
|
||||
▼
|
||||
┌───────────┐
|
||||
│ Database │
|
||||
│ (SQLite) │
|
||||
└───────────┘
|
||||
```
|
||||
|
||||
## File Organization by Feature
|
||||
|
||||
### Shop/Inventory
|
||||
- `backend/src/routes/shop/` - Shop API routes
|
||||
- `backend/src/models/Item/` - Item model
|
||||
- `backend/src/controllers/shop/` - Shop controller
|
||||
- `frontend/src/components/Shop/` - Shop UI components
|
||||
|
||||
### Bestiary
|
||||
- `backend/src/routes/bestiary/` - Bestiary routes
|
||||
- `backend/src/models/Creature/` - Creature model
|
||||
- `backend/src/controllers/bestiary/` - Bestiary controller
|
||||
- `frontend/src/components/Bestiary/` - Bestiary UI
|
||||
|
||||
### Players
|
||||
- `backend/src/routes/players/` - Player routes
|
||||
- `backend/src/models/Player/` - Player model
|
||||
- `backend/src/controllers/players/` - Player controller
|
||||
- `frontend/src/components/Players/` - Player UI (GM only)
|
||||
@@ -0,0 +1,52 @@
|
||||
# Deathwatch Roller Documentation
|
||||
|
||||
## Overview
|
||||
|
||||
This documentation covers the Deathwatch Roller application - a React-based web app for managing Deathwatch tabletop RPG sessions.
|
||||
|
||||
## Getting Started
|
||||
|
||||
- [Quick Start Guide](./quick-start.md) - Installation and setup
|
||||
- [User Guide](./user-guide.md) - Features and how to use the app
|
||||
- [API Documentation](./api.md) - REST API reference
|
||||
- [Developer Guide](./developer-guide.md) - Contributing to the project
|
||||
- [Troubleshooting](./troubleshooting.md) - Common issues and solutions
|
||||
- [Security Guide](./security.md) - Security best practices
|
||||
- [PM2 Configuration](./pm2.md) - Production deployment
|
||||
|
||||
## Project Structure
|
||||
|
||||
```
|
||||
dwroller/
|
||||
├── backend/ # Backend API server
|
||||
├── frontend/ # React frontend application
|
||||
├── data/ # Static data and assets
|
||||
├── database/ # Database backups and migrations
|
||||
└── docs/ # This documentation
|
||||
```
|
||||
|
||||
## Key Features
|
||||
|
||||
- **Player Management** - Full CRUD operations for player accounts
|
||||
- **Requisition Shop** - Browse and purchase items with RP
|
||||
- **Session Tracking** - Track game sessions and player progress
|
||||
- **GM Kit** - Game Master tools and utilities
|
||||
- **Bestiary** - Monster and enemy reference
|
||||
- **Rules Database** - Searchable rules and mechanics
|
||||
|
||||
## API Endpoints
|
||||
|
||||
| Endpoint | Description |
|
||||
|----------|-------------|
|
||||
| `GET /api/players` | Get all players |
|
||||
| `POST /api/players/gm/*` | GM-only operations |
|
||||
| `GET /api/shop` | Get shop inventory |
|
||||
| `GET /api/bestiary` | Get bestiary data |
|
||||
|
||||
## Contributing
|
||||
|
||||
See [Contributing Guide](./contributing.md) for details on how to contribute.
|
||||
|
||||
## License
|
||||
|
||||
MIT License - See [LICENSE](../../LICENSE) for details.
|
||||
+407
@@ -0,0 +1,407 @@
|
||||
# API Documentation
|
||||
|
||||
## Base URL
|
||||
|
||||
```
|
||||
http://localhost:5000/api
|
||||
```
|
||||
|
||||
## Authentication
|
||||
|
||||
Most endpoints require authentication via session cookies.
|
||||
|
||||
### Session Management
|
||||
|
||||
```bash
|
||||
# Login (example)
|
||||
POST /api/auth/login
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"email": "[email protected]",
|
||||
"password": "secret"
|
||||
}
|
||||
```
|
||||
|
||||
## Players Endpoints
|
||||
|
||||
### Get All Players
|
||||
|
||||
```bash
|
||||
GET /api/players
|
||||
```
|
||||
|
||||
**Response:**
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"id": 1,
|
||||
"name": "Player One",
|
||||
"xp": 1500,
|
||||
"rp": 500,
|
||||
"renown": 10,
|
||||
"createdAt": "2025-08-16T10:00:00Z"
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
### Create Player (GM Only)
|
||||
|
||||
```bash
|
||||
POST /api/players
|
||||
Authorization: Bearer {gm-token}
|
||||
```
|
||||
|
||||
**Request:**
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "New Player",
|
||||
"xp": 0,
|
||||
"rp": 100,
|
||||
"renown": 0
|
||||
}
|
||||
```
|
||||
|
||||
### Update Player
|
||||
|
||||
```bash
|
||||
PUT /api/players/:id
|
||||
Authorization: Bearer {gm-token}
|
||||
```
|
||||
|
||||
**Request:**
|
||||
|
||||
```json
|
||||
{
|
||||
"xp": 1600,
|
||||
"rp": 600
|
||||
}
|
||||
```
|
||||
|
||||
### Delete Player (GM Only)
|
||||
|
||||
```bash
|
||||
DELETE /api/players/:id
|
||||
Authorization: Bearer {gm-token}
|
||||
```
|
||||
|
||||
### Reset Password
|
||||
|
||||
```bash
|
||||
POST /api/players/:id/reset-password
|
||||
Authorization: Bearer {gm-token}
|
||||
```
|
||||
|
||||
## Shop Endpoints
|
||||
|
||||
### Get Shop Inventory
|
||||
|
||||
```bash
|
||||
GET /api/shop
|
||||
```
|
||||
|
||||
**Response:**
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"id": 1,
|
||||
"name": "Blade",
|
||||
"type": "weapon",
|
||||
"cost": 100,
|
||||
"stats": {
|
||||
"damage": "1d10",
|
||||
"weight": 3
|
||||
}
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
### Purchase Item
|
||||
|
||||
```bash
|
||||
POST /api/shop/purchase
|
||||
```
|
||||
|
||||
**Request:**
|
||||
|
||||
```json
|
||||
{
|
||||
"itemId": 1,
|
||||
"quantity": 1
|
||||
}
|
||||
```
|
||||
|
||||
### Get Player Inventory
|
||||
|
||||
```bash
|
||||
GET /api/inventory
|
||||
```
|
||||
|
||||
**Response:**
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"itemId": 1,
|
||||
"item": {
|
||||
"name": "Blade",
|
||||
"stats": {...}
|
||||
},
|
||||
"quantity": 1
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
## Bestiary Endpoints
|
||||
|
||||
### Get All Creatures
|
||||
|
||||
```bash
|
||||
GET /api/bestiary
|
||||
```
|
||||
|
||||
**Response:**
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"id": 1,
|
||||
"name": "Ghoul",
|
||||
"type": "abomination",
|
||||
"hp": 45,
|
||||
"armor": 10,
|
||||
"weaknesses": ["acid", "piercing"],
|
||||
"description": "Corrupted abomination..."
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
### Get Creature by ID
|
||||
|
||||
```bash
|
||||
GET /api/bestiary/:id
|
||||
```
|
||||
|
||||
### Search Creatures
|
||||
|
||||
```bash
|
||||
GET /api/bestiary/search?query=ghoul&type=abomination
|
||||
```
|
||||
|
||||
## Rules Endpoints
|
||||
|
||||
### Search Rules
|
||||
|
||||
```bash
|
||||
GET /api/rules/search?query=damage
|
||||
```
|
||||
|
||||
**Response:**
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"title": "Damage Mechanics",
|
||||
"content": "...",
|
||||
"page": "combat_damage"
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
### Get Rules Page
|
||||
|
||||
```bash
|
||||
GET /api/rules/page/:pageId
|
||||
```
|
||||
|
||||
### Print Rulesheet
|
||||
|
||||
```bash
|
||||
GET /api/rules/print/quick-reference
|
||||
```
|
||||
|
||||
Returns printable HTML for the quick reference.
|
||||
|
||||
## Sessions Endpoints
|
||||
|
||||
### Create Session
|
||||
|
||||
```bash
|
||||
POST /api/sessions
|
||||
```
|
||||
|
||||
**Request:**
|
||||
|
||||
```json
|
||||
{
|
||||
"title": "Session 1",
|
||||
"date": "2026-04-19",
|
||||
"notes": "First session",
|
||||
"location": "The Ruins"
|
||||
}
|
||||
```
|
||||
|
||||
### Get Sessions
|
||||
|
||||
```bash
|
||||
GET /api/sessions?date=2026-04-19
|
||||
```
|
||||
|
||||
### Get Session by ID
|
||||
|
||||
```bash
|
||||
GET /api/sessions/:id
|
||||
```
|
||||
|
||||
### Update Session
|
||||
|
||||
```bash
|
||||
PUT /api/sessions/:id
|
||||
```
|
||||
|
||||
**Request:**
|
||||
|
||||
```json
|
||||
{
|
||||
"notes": "Updated notes",
|
||||
"location": "The Ruins"
|
||||
}
|
||||
```
|
||||
|
||||
### Delete Session
|
||||
|
||||
```bash
|
||||
DELETE /api/sessions/:id
|
||||
```
|
||||
|
||||
## Files Endpoints
|
||||
|
||||
### Upload File
|
||||
|
||||
```bash
|
||||
POST /api/files/upload
|
||||
Content-Type: multipart/form-data
|
||||
```
|
||||
|
||||
**Form Data:**
|
||||
|
||||
- `file`: The file to upload
|
||||
|
||||
### Get File
|
||||
|
||||
```bash
|
||||
GET /api/files/:filename
|
||||
```
|
||||
|
||||
### Delete File
|
||||
|
||||
```bash
|
||||
DELETE /api/files/:filename
|
||||
```
|
||||
|
||||
### List Files
|
||||
|
||||
```bash
|
||||
GET /api/files
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
### Standard Responses
|
||||
|
||||
```json
|
||||
{
|
||||
"success": false,
|
||||
"error": {
|
||||
"code": "ERROR_CODE",
|
||||
"message": "Human readable error",
|
||||
"details": "Additional details"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Common Errors
|
||||
|
||||
| Code | Description |
|
||||
|------|-------------|
|
||||
| `UNAUTHORIZED` | Invalid or missing session |
|
||||
| `FORBIDDEN` | Insufficient permissions |
|
||||
| `NOT_FOUND` | Resource doesn't exist |
|
||||
| `VALIDATION_ERROR` | Invalid input data |
|
||||
| `INTERNAL_ERROR` | Server error |
|
||||
|
||||
### Error Examples
|
||||
|
||||
```json
|
||||
{
|
||||
"success": false,
|
||||
"error": {
|
||||
"code": "VALIDATION_ERROR",
|
||||
"message": "Invalid input",
|
||||
"details": [
|
||||
{
|
||||
"field": "email",
|
||||
"message": "Invalid email format"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Rate Limiting
|
||||
|
||||
API calls are rate limited:
|
||||
- **100 requests per minute** per IP
|
||||
- Headers include:
|
||||
- `X-RateLimit-Limit: 100`
|
||||
- `X-RateLimit-Remaining: 99`
|
||||
|
||||
## Webhooks
|
||||
|
||||
Webhooks can be configured for events:
|
||||
- Player created
|
||||
- Item purchased
|
||||
- Session created
|
||||
|
||||
```bash
|
||||
POST /api/webhooks
|
||||
```
|
||||
|
||||
**Request:**
|
||||
|
||||
```json
|
||||
{
|
||||
"url": "https://example.com/webhook",
|
||||
"events": ["player.created", "item.purchased"],
|
||||
"secret": "your-webhook-secret"
|
||||
}
|
||||
```
|
||||
|
||||
## Testing with curl
|
||||
|
||||
### Get Players
|
||||
|
||||
```bash
|
||||
curl http://localhost:5000/api/players
|
||||
```
|
||||
|
||||
### Get Shop
|
||||
|
||||
```bash
|
||||
curl http://localhost:5000/api/shop
|
||||
```
|
||||
|
||||
### Get Bestiary
|
||||
|
||||
```bash
|
||||
curl http://localhost:5000/api/bestiary
|
||||
```
|
||||
|
||||
### Search Rules
|
||||
|
||||
```bash
|
||||
curl "http://localhost:5000/api/rules/search?query=damage"
|
||||
```
|
||||
@@ -0,0 +1,275 @@
|
||||
# Contributing Guide
|
||||
|
||||
Thank you for your interest in contributing to Deathwatch Roller! This guide will help you get started.
|
||||
|
||||
## How to Contribute
|
||||
|
||||
### Reporting Bugs
|
||||
|
||||
1. Check if the bug already exists in [issues](https://github.com/alexpolo1/dwroller/issues)
|
||||
2. Create a new issue with:
|
||||
- Clear title and description
|
||||
- Steps to reproduce
|
||||
- Expected vs actual behavior
|
||||
- Screenshots if applicable
|
||||
- Your environment (Node version, OS)
|
||||
|
||||
### Suggesting Features
|
||||
|
||||
1. Check [issues](https://github.com/alexpolo1/dwroller/issues) for similar suggestions
|
||||
2. Create a feature request issue
|
||||
3. Include use case and expected behavior
|
||||
|
||||
### Pull Requests
|
||||
|
||||
1. Fork the repository
|
||||
2. Create a branch (`git checkout -b feature/amazing-feature`)
|
||||
3. Make your changes
|
||||
4. Add tests for new functionality
|
||||
5. Update documentation
|
||||
6. Commit with descriptive messages
|
||||
7. Push to your branch
|
||||
8. Open a pull request
|
||||
|
||||
```bash
|
||||
git checkout -b feature/amazing-feature
|
||||
npm install
|
||||
npm run dev
|
||||
# Make changes
|
||||
git add .
|
||||
git commit -m "feat: add amazing feature"
|
||||
git push origin feature/amazing-feature
|
||||
```
|
||||
|
||||
## Development Workflow
|
||||
|
||||
### Setting Up Your Environment
|
||||
|
||||
```bash
|
||||
# Clone the repository
|
||||
git clone https://github.com/alexpolo1/dwroller.git
|
||||
cd dwroller
|
||||
|
||||
# Install dependencies
|
||||
npm install
|
||||
|
||||
# Start development servers
|
||||
npm run dev
|
||||
```
|
||||
|
||||
### Code Style
|
||||
|
||||
We follow these style guidelines:
|
||||
|
||||
- **TypeScript**: Use TypeScript for all new code
|
||||
- **ESLint**: Run `npm run lint` before committing
|
||||
- **Prettier**: Auto-format with `npm run format`
|
||||
- **Testing**: Add tests for all new features
|
||||
|
||||
### Commit Guidelines
|
||||
|
||||
Use conventional commits:
|
||||
|
||||
```
|
||||
feat: New feature
|
||||
fix: Bug fix
|
||||
docs: Documentation
|
||||
style: Code style changes
|
||||
refactor: Code refactoring
|
||||
test: Adding tests
|
||||
chore: Maintenance tasks
|
||||
```
|
||||
|
||||
## Code Review Process
|
||||
|
||||
All PRs go through:
|
||||
|
||||
1. **Initial Review**: Automated checks (lint, tests)
|
||||
2. **Technical Review**: Code quality, design, performance
|
||||
3. **Documentation**: Are docs updated?
|
||||
4. **Approval**: Maintainer approval
|
||||
|
||||
### Review Checklist
|
||||
|
||||
- [ ] Tests pass
|
||||
- [ ] No lint errors
|
||||
- [ ] Code follows style guide
|
||||
- [ ] Documentation updated
|
||||
- [ ] No security issues
|
||||
- [ ] Performance considerations
|
||||
|
||||
## Documentation
|
||||
|
||||
Documentation is in the `docs/` folder:
|
||||
|
||||
- `SUMMARY.md` - Table of contents
|
||||
- `quick-start.md` - Setup guide
|
||||
- `user-guide.md` - User documentation
|
||||
- `api.md` - API reference
|
||||
- `developer-guide.md` - Dev guide
|
||||
|
||||
When adding features:
|
||||
- Update relevant documentation
|
||||
- Add code examples
|
||||
- Include usage instructions
|
||||
|
||||
## Testing
|
||||
|
||||
### Backend Tests
|
||||
|
||||
```bash
|
||||
# Run all tests
|
||||
npm test
|
||||
|
||||
# Run with coverage
|
||||
npm run test:coverage
|
||||
|
||||
# Watch mode
|
||||
npm run test:watch
|
||||
```
|
||||
|
||||
### Frontend Tests
|
||||
|
||||
```bash
|
||||
# Run tests
|
||||
cd frontend
|
||||
npm test
|
||||
|
||||
# Run with coverage
|
||||
npm run test:coverage
|
||||
```
|
||||
|
||||
### E2E Tests
|
||||
|
||||
```bash
|
||||
# E2E testing (optional)
|
||||
npm run test:e2e
|
||||
```
|
||||
|
||||
## Code Quality
|
||||
|
||||
### Linting
|
||||
|
||||
```bash
|
||||
# Check for issues
|
||||
npm run lint
|
||||
|
||||
# Fix automatically fixable issues
|
||||
npm run lint:fix
|
||||
```
|
||||
|
||||
### Formatting
|
||||
|
||||
```bash
|
||||
# Format code
|
||||
npm run format
|
||||
```
|
||||
|
||||
### Building
|
||||
|
||||
```bash
|
||||
# Build for production
|
||||
npm run build
|
||||
|
||||
# Check build
|
||||
npm run build
|
||||
```
|
||||
|
||||
## Release Process
|
||||
|
||||
1. Update `CHANGELOG.md`
|
||||
2. Bump version in `package.json`
|
||||
3. Create release branch
|
||||
4. Tag release
|
||||
5. Publish to npm (if applicable)
|
||||
|
||||
```bash
|
||||
npm version patch
|
||||
git push --follow-tags
|
||||
```
|
||||
|
||||
## Performance Guidelines
|
||||
|
||||
- Use indexing for database queries
|
||||
- Implement caching for static data
|
||||
- Lazy load heavy components
|
||||
- Optimize bundle size
|
||||
- Monitor performance metrics
|
||||
|
||||
### Before/After Example
|
||||
|
||||
```typescript
|
||||
// ❌ BAD: N+1 query problem
|
||||
const items = await db.items.findAll();
|
||||
items.forEach(item => {
|
||||
const owner = await db.owners.findOne({ where: { itemId: item.id } });
|
||||
});
|
||||
|
||||
// ✅ GOOD: Use include for eager loading
|
||||
const items = await db.items.findAll({
|
||||
include: [{ model: Owner }]
|
||||
});
|
||||
```
|
||||
|
||||
## Security Guidelines
|
||||
|
||||
- Never commit secrets or credentials
|
||||
- Use environment variables for sensitive data
|
||||
- Review third-party dependencies
|
||||
- Use prepared statements for SQL
|
||||
- Implement proper authentication
|
||||
|
||||
### Secrets Management
|
||||
|
||||
```bash
|
||||
# ✅ GOOD
|
||||
export SESSION_SECRET=${SESSION_SECRET}
|
||||
|
||||
# ❌ BAD - Never do this
|
||||
SESSION_SECRET="hardcoded-secret"
|
||||
```
|
||||
|
||||
## Accessibility
|
||||
|
||||
Make sure your changes:
|
||||
- Support keyboard navigation
|
||||
- Include ARIA labels where needed
|
||||
- Maintain color contrast
|
||||
- Support screen readers
|
||||
- Are responsive
|
||||
|
||||
## Browser Support
|
||||
|
||||
Test on:
|
||||
- Chrome/Edge (Chromium)
|
||||
- Firefox
|
||||
- Safari
|
||||
- Mobile browsers
|
||||
|
||||
## Performance Benchmarks
|
||||
|
||||
- Initial load: < 2s
|
||||
- API response: < 200ms
|
||||
- Bundle size: < 500kb (gzipped)
|
||||
- Lighthouse score: > 90
|
||||
|
||||
## Community
|
||||
|
||||
- [GitHub Issues](https://github.com/alexpolo1/dwroller/issues)
|
||||
- [Pull Requests](https://github.com/alexpolo1/dwroller/pulls)
|
||||
- Contributing guidelines: `CONTRIBUTING.md`
|
||||
|
||||
## Code of Conduct
|
||||
|
||||
- Be respectful
|
||||
- Welcome newcomers
|
||||
- Provide constructive feedback
|
||||
- Focus on the project
|
||||
|
||||
## License
|
||||
|
||||
By contributing, you agree that your contributions will be licensed under the MIT License.
|
||||
|
||||
## Questions?
|
||||
|
||||
Open an issue or start a discussion on GitHub.
|
||||
@@ -0,0 +1,439 @@
|
||||
# Developer Guide
|
||||
|
||||
## Project Structure
|
||||
|
||||
```
|
||||
dwroller/
|
||||
├── backend/
|
||||
│ ├── src/
|
||||
│ │ ├── routes/ # API routes
|
||||
│ │ ├── models/ # Data models
|
||||
│ │ ├── controllers/ # Route controllers
|
||||
│ │ ├── middleware/ # Auth, validation
|
||||
│ │ └── utils/ # Helper functions
|
||||
│ ├── server.js # Entry point
|
||||
│ └── package.json
|
||||
├── frontend/
|
||||
│ ├── src/
|
||||
│ │ ├── components/ # React components
|
||||
│ │ ├── pages/ # Route pages
|
||||
│ │ ├── hooks/ # Custom hooks
|
||||
│ │ └── store/ # State management
|
||||
│ ├── public/ # Static assets
|
||||
│ ├── package.json
|
||||
│ └── vite.config.js
|
||||
├── data/ # Static data files
|
||||
├── database/ # Database files
|
||||
└── docs/ # Documentation
|
||||
```
|
||||
|
||||
## Development Setup
|
||||
|
||||
### Backend
|
||||
|
||||
```bash
|
||||
cd backend
|
||||
npm install
|
||||
npm run dev
|
||||
```
|
||||
|
||||
The backend starts on port 5000.
|
||||
|
||||
### Frontend
|
||||
|
||||
```bash
|
||||
cd frontend
|
||||
npm install
|
||||
npm run dev
|
||||
```
|
||||
|
||||
The frontend starts on port 3000.
|
||||
|
||||
## Code Style
|
||||
|
||||
### JavaScript/TypeScript
|
||||
|
||||
- Use TypeScript for new code
|
||||
- Follow Airbnb JavaScript style guide
|
||||
- Maximum 100 character line length
|
||||
- Use ESLint for linting
|
||||
|
||||
```typescript
|
||||
// Example code style
|
||||
interface Player {
|
||||
id: number;
|
||||
name: string;
|
||||
xp: number;
|
||||
rp: number;
|
||||
}
|
||||
|
||||
export class PlayerService {
|
||||
async getPlayer(id: number): Promise<Player> {
|
||||
const player = await this.repository.findById(id);
|
||||
return this.transform(player);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### React
|
||||
|
||||
- Functional components with hooks
|
||||
- Use TypeScript for props
|
||||
- Follow React best practices
|
||||
- Use Context for global state
|
||||
|
||||
```typescript
|
||||
// Example React component
|
||||
interface ShopItemProps {
|
||||
item: ShopItem;
|
||||
onPurchase: (itemId: number) => void;
|
||||
}
|
||||
|
||||
const ShopItem: React.FC<ShopItemProps> = ({ item, onPurchase }) => {
|
||||
return (
|
||||
<div className="shop-item">
|
||||
<h3>{item.name}</h3>
|
||||
<p>Cost: {item.cost} RP</p>
|
||||
<button onClick={() => onPurchase(item.id)}>
|
||||
Purchase
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
## Database
|
||||
|
||||
### Schema
|
||||
|
||||
The database uses SQLite by default. The schema is created automatically on first run.
|
||||
|
||||
Main tables:
|
||||
- `players` - Player accounts
|
||||
- `items` - Shop inventory
|
||||
- `inventory` - Player item ownership
|
||||
- `sessions` - Game session tracking
|
||||
- `webhooks` - Webhook configurations
|
||||
|
||||
### Migrations
|
||||
|
||||
Migrations are stored in `backend/migrations/`.
|
||||
|
||||
```bash
|
||||
# Run migrations
|
||||
npm run migrate
|
||||
|
||||
# Create new migration
|
||||
npx knex migrate:make create_new_table
|
||||
```
|
||||
|
||||
## API Development
|
||||
|
||||
### Creating a New Endpoint
|
||||
|
||||
```bash
|
||||
backend/src/routes/new-endpoints.ts
|
||||
```
|
||||
|
||||
```typescript
|
||||
import express, { Request, Response } from 'express';
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
// GET /new-endpoint
|
||||
router.get('/', async (req: Request, res: Response) => {
|
||||
const data = await getNewData();
|
||||
res.json({ success: true, data });
|
||||
});
|
||||
|
||||
export default router;
|
||||
```
|
||||
|
||||
Add to `server.js`:
|
||||
|
||||
```typescript
|
||||
import newEndpoints from './src/routes/new-endpoints';
|
||||
|
||||
app.use('/api/new', newEndpoints);
|
||||
```
|
||||
|
||||
### Adding Authentication
|
||||
|
||||
```typescript
|
||||
import { requireAuth } from '../middleware/auth';
|
||||
|
||||
router.get(protectedRoute, requireAuth, async (req, res) => {
|
||||
// Protected route logic
|
||||
});
|
||||
```
|
||||
|
||||
## Frontend Development
|
||||
|
||||
### Creating a New Component
|
||||
|
||||
```bash
|
||||
frontend/src/components/NewComponent.tsx
|
||||
```
|
||||
|
||||
```typescript
|
||||
import React from 'react';
|
||||
|
||||
interface NewComponentProps {
|
||||
data: any;
|
||||
onAction: (action: string) => void;
|
||||
}
|
||||
|
||||
export const NewComponent: React.FC<NewComponentProps> = ({
|
||||
data,
|
||||
onAction,
|
||||
}) => {
|
||||
const handleClick = () => {
|
||||
onAction('click');
|
||||
};
|
||||
|
||||
return <div className="new-component">{data.title}</div>;
|
||||
};
|
||||
```
|
||||
|
||||
### State Management
|
||||
|
||||
Use React Context for global state:
|
||||
|
||||
```typescript
|
||||
// frontend/src/store/PlayerContext.tsx
|
||||
import React, { createContext, useContext, useState } from 'react';
|
||||
|
||||
interface PlayerState {
|
||||
players: Player[];
|
||||
addPlayer: (player: Player) => void;
|
||||
}
|
||||
|
||||
const PlayerContext = createContext<PlayerState | undefined>(undefined);
|
||||
|
||||
export const PlayerProvider: React.FC<{ children: React.ReactNode }> = ({
|
||||
children,
|
||||
}) => {
|
||||
const [players, setPlayers] = useState<Player[]>([]);
|
||||
|
||||
const addPlayer = (player: Player) => {
|
||||
setPlayers((prev) => [...prev, player]);
|
||||
};
|
||||
|
||||
return (
|
||||
<PlayerContext.Provider value={{ players, addPlayer }}>
|
||||
{children}
|
||||
</PlayerContext.Provider>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
## Testing
|
||||
|
||||
### Backend Tests
|
||||
|
||||
```bash
|
||||
backend/src/routes/__tests__/new-endpoints.test.ts
|
||||
```
|
||||
|
||||
```typescript
|
||||
import request from 'supertest';
|
||||
import app from '../../../server';
|
||||
|
||||
describe('New Endpoint', () => {
|
||||
it('should return 200 for GET', async () => {
|
||||
const res = await request(app).get('/new-endpoint');
|
||||
|
||||
expect(res.statusCode).toBe(200);
|
||||
expect(res.body.success).toBe(true);
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
Run tests:
|
||||
|
||||
```bash
|
||||
npm test
|
||||
```
|
||||
|
||||
### Frontend Tests
|
||||
|
||||
```bash
|
||||
frontend/src/components/__tests__/NewComponent.test.tsx
|
||||
```
|
||||
|
||||
```typescript
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import NewComponent from '../NewComponent';
|
||||
|
||||
describe('NewComponent', () => {
|
||||
it('renders the title', () => {
|
||||
render(<NewComponent data={{ title: 'Test' }} />);
|
||||
|
||||
expect(screen.getByText('Test')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
Run tests:
|
||||
|
||||
```bash
|
||||
npm test
|
||||
```
|
||||
|
||||
## Debugging
|
||||
|
||||
### Backend Debugging
|
||||
|
||||
```bash
|
||||
# Enable verbose logging
|
||||
NODE_ENV=development npm run server
|
||||
|
||||
# View logs
|
||||
tail -f backend.log
|
||||
```
|
||||
|
||||
### Frontend Debugging
|
||||
|
||||
```bash
|
||||
# Open DevTools
|
||||
npm run dev
|
||||
|
||||
# Check browser console
|
||||
# Network tab for API calls
|
||||
```
|
||||
|
||||
## Deployment
|
||||
|
||||
### Build
|
||||
|
||||
```bash
|
||||
# Build frontend
|
||||
npm run build
|
||||
|
||||
# Build backend
|
||||
cd backend
|
||||
npm run build
|
||||
```
|
||||
|
||||
### PM2 Setup
|
||||
|
||||
```bash
|
||||
# Start with PM2
|
||||
npm run pm2:start
|
||||
|
||||
# View logs
|
||||
pm2 logs
|
||||
|
||||
# Stop
|
||||
npm run pm2:stop
|
||||
```
|
||||
|
||||
### Production Environment Variables
|
||||
|
||||
```bash
|
||||
# Create production env
|
||||
cp .env.example .env.production
|
||||
|
||||
# Set required variables:
|
||||
# - NODE_ENV=production
|
||||
# - SESSION_SECRET
|
||||
# - X_GM_SECRET
|
||||
# - PORT
|
||||
```
|
||||
|
||||
## Performance
|
||||
|
||||
### Optimization Tips
|
||||
|
||||
1. **Database**: Use indexes for frequent queries
|
||||
2. **Frontend**: Enable code splitting
|
||||
3. **API**: Implement caching for static data
|
||||
4. **Images**: Use lazy loading
|
||||
|
||||
### Caching
|
||||
|
||||
```typescript
|
||||
// Example: Cache shop data
|
||||
const CACHE_DURATION = 5 * 60 * 1000; // 5 minutes
|
||||
|
||||
async function getShop() {
|
||||
const cached = getFromCache('shop');
|
||||
if (cached) return cached;
|
||||
|
||||
const shop = await db.items.findAll();
|
||||
setCache('shop', shop, CACHE_DURATION);
|
||||
return shop;
|
||||
}
|
||||
```
|
||||
|
||||
## Security
|
||||
|
||||
### Authentication
|
||||
|
||||
All API endpoints use session-based authentication. Implement proper session management:
|
||||
|
||||
```typescript
|
||||
// Session middleware
|
||||
import { verifyToken } from '../utils/auth';
|
||||
|
||||
export const requireAuth = async (req: Request, res: Response, next: NextFunction) => {
|
||||
const session = req.headers['cookie'];
|
||||
|
||||
if (!session) {
|
||||
return res.status(401).json({ error: 'Unauthorized' });
|
||||
}
|
||||
|
||||
try {
|
||||
req.user = await verifyToken(session);
|
||||
next();
|
||||
} catch (error) {
|
||||
return res.status(401).json({ error: 'Invalid session' });
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### API Security
|
||||
|
||||
1. Use HTTPS in production
|
||||
2. Rate limit API requests
|
||||
3. Validate all inputs
|
||||
4. Use prepared statements for SQL
|
||||
5. Never expose sensitive data
|
||||
|
||||
## Contributing
|
||||
|
||||
### Pull Request Process
|
||||
|
||||
1. Fork the repository
|
||||
2. Create a feature branch
|
||||
3. Make your changes
|
||||
4. Add tests for new functionality
|
||||
5. Update documentation
|
||||
6. Submit a pull request
|
||||
|
||||
### Code Review
|
||||
|
||||
All PRs will be reviewed for:
|
||||
- Code quality
|
||||
- Test coverage
|
||||
- Documentation
|
||||
- Security
|
||||
- Performance
|
||||
|
||||
### Commit Messages
|
||||
|
||||
Use conventional commits:
|
||||
|
||||
```bash
|
||||
feat: add new shop item filter
|
||||
fix: resolve authentication issue
|
||||
docs: update API documentation
|
||||
chore: update dependencies
|
||||
```
|
||||
|
||||
## Resources
|
||||
|
||||
- [React Documentation](https://react.dev/)
|
||||
- [Express.js Documentation](https://expressjs.com/)
|
||||
- [TypeScript Handbook](https://www.typescriptlang.org/docs/)
|
||||
- [SQLite Documentation](https://www.sqlite.org/docs.html)
|
||||
+188
@@ -0,0 +1,188 @@
|
||||
# PM2 Configuration
|
||||
|
||||
This document explains how to use PM2 for managing Deathwatch Roller in production.
|
||||
|
||||
## What is PM2?
|
||||
|
||||
PM2 is a production process manager for Node.js that keeps your application running 24/7.
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
# Install PM2 globally
|
||||
npm install -g pm2
|
||||
|
||||
# Initialize PM2 ecosystem file
|
||||
pm2 init
|
||||
```
|
||||
|
||||
## Application Configuration
|
||||
|
||||
Create `ecosystem.config.js`:
|
||||
|
||||
```javascript
|
||||
module.exports = {
|
||||
apps: [
|
||||
{
|
||||
name: 'dwroller-frontend',
|
||||
cwd: './frontend',
|
||||
script: 'npm',
|
||||
args: 'start',
|
||||
env: {
|
||||
NODE_ENV: 'production',
|
||||
PORT: 3000
|
||||
},
|
||||
env_development: {
|
||||
NODE_ENV: 'development'
|
||||
},
|
||||
instances: 1,
|
||||
autorestart: true,
|
||||
max_memory_restart: '500M'
|
||||
},
|
||||
{
|
||||
name: 'dwroller-backend',
|
||||
cwd: './backend',
|
||||
script: 'npm',
|
||||
args: 'start',
|
||||
env: {
|
||||
NODE_ENV: 'production',
|
||||
PORT: 5000
|
||||
},
|
||||
env_development: {
|
||||
NODE_ENV: 'development'
|
||||
},
|
||||
instances: 1,
|
||||
autorestart: true,
|
||||
max_memory_restart: '500M'
|
||||
}
|
||||
]
|
||||
};
|
||||
```
|
||||
|
||||
## Starting the Application
|
||||
|
||||
```bash
|
||||
# Start both apps
|
||||
pm2 start ecosystem.config.js
|
||||
|
||||
# Start only backend
|
||||
pm2 start ecosystem.config.js --only backend
|
||||
|
||||
# Start only frontend
|
||||
pm2 start ecosystem.config.js --only frontend
|
||||
```
|
||||
|
||||
## Managing Apps
|
||||
|
||||
```bash
|
||||
# View status
|
||||
pm2 list
|
||||
|
||||
# View logs
|
||||
pm2 logs
|
||||
|
||||
# Restart app
|
||||
pm2 restart all
|
||||
|
||||
# Stop app
|
||||
pm2 stop all
|
||||
|
||||
# Delete app
|
||||
pm2 delete all
|
||||
```
|
||||
|
||||
## Monitoring
|
||||
|
||||
```bash
|
||||
# Enable monitoring
|
||||
pm2 monitortype --interval 10000
|
||||
|
||||
# View stats
|
||||
pm2 stats
|
||||
```
|
||||
|
||||
## Logging
|
||||
|
||||
```bash
|
||||
# Rotate logs
|
||||
pm2 rotate logs
|
||||
|
||||
# Save log
|
||||
pm2 save
|
||||
|
||||
# Load saved PM2 process list
|
||||
pm2 startup
|
||||
pm2 save
|
||||
```
|
||||
|
||||
## NPM Scripts
|
||||
|
||||
Update `package.json` with PM2 scripts:
|
||||
|
||||
```json
|
||||
{
|
||||
"scripts": {
|
||||
"start": "npm run build && pm2 start ecosystem.config.js",
|
||||
"pm2:start": "pm2 start ecosystem.config.js",
|
||||
"pm2:stop": "pm2 stop all",
|
||||
"pm2:restart": "pm2 restart all",
|
||||
"pm2:logs": "pm2 logs",
|
||||
"pm2:list": "pm2 list",
|
||||
"pm2:save": "pm2 save",
|
||||
"pm2:startup": "pm2 startup"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Production Build
|
||||
|
||||
```bash
|
||||
# Build frontend
|
||||
cd frontend
|
||||
npm run build
|
||||
|
||||
# Start with PM2
|
||||
cd ../..
|
||||
npm run pm2:start
|
||||
```
|
||||
|
||||
## Environment Variables
|
||||
|
||||
Store secrets in `.env` or use PM2 env:
|
||||
|
||||
```bash
|
||||
# Set environment variable
|
||||
pm2 set ecosystem.config.js:* env:SESSION_SECRET="your-secret"
|
||||
|
||||
# List all env variables
|
||||
pm2 env ecosystem.config.js
|
||||
```
|
||||
|
||||
## Backup Strategy
|
||||
|
||||
```bash
|
||||
# Create database backup
|
||||
npm run backup
|
||||
|
||||
# Schedule backup with cron
|
||||
0 2 * * * /path/to/db/backup.sh
|
||||
```
|
||||
|
||||
## Scaling
|
||||
|
||||
For horizontal scaling, configure multiple instances:
|
||||
|
||||
```javascript
|
||||
{
|
||||
instances: 'max',
|
||||
exec_mode: 'cluster',
|
||||
watch: false
|
||||
}
|
||||
```
|
||||
|
||||
## Auto-restart on reboot
|
||||
|
||||
```bash
|
||||
pm2 startup
|
||||
pm2 save
|
||||
```
|
||||
@@ -0,0 +1,127 @@
|
||||
# Quick Start Guide
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Before you begin, ensure you have:
|
||||
|
||||
- **Node.js** (v18+ recommended) - Check with `node --version`
|
||||
- **npm** or **yarn** - Package manager
|
||||
- **Git** - For version control
|
||||
|
||||
## Installation
|
||||
|
||||
### 1. Clone the Repository
|
||||
|
||||
```bash
|
||||
git clone https://github.com/alexpolo1/dwroller.git
|
||||
cd dwroller
|
||||
```
|
||||
|
||||
### 2. Install Dependencies
|
||||
|
||||
```bash
|
||||
npm install
|
||||
```
|
||||
|
||||
This will install all required packages for both frontend and backend.
|
||||
|
||||
### 3. Configure Environment
|
||||
|
||||
Create a `.env` file in the root directory:
|
||||
|
||||
```bash
|
||||
cp .env.example .env
|
||||
```
|
||||
|
||||
Edit `.env` with your configuration:
|
||||
|
||||
```env
|
||||
# Backend server port
|
||||
PORT=5000
|
||||
|
||||
# Session secret (random string recommended)
|
||||
SESSION_SECRET=your-random-secret-here
|
||||
|
||||
# GM API secret for player management
|
||||
X_GM_SECRET=your-gm-secret-key
|
||||
|
||||
# Database path (optional - defaults to server location)
|
||||
DATABASE_PATH=./database/data.db
|
||||
```
|
||||
|
||||
### 4. Start the Application
|
||||
|
||||
Run both servers:
|
||||
|
||||
```bash
|
||||
# Terminal 1 - Backend API
|
||||
npm run server
|
||||
|
||||
# Terminal 2 - Frontend
|
||||
npm start
|
||||
```
|
||||
|
||||
The application will be available at:
|
||||
- **Frontend**: `http://localhost:3000`
|
||||
- **API**: `http://localhost:5000`
|
||||
|
||||
## First Steps
|
||||
|
||||
1. Visit `http://localhost:3000` in your browser
|
||||
2. Browse the shop or view the bestiary
|
||||
3. If you're a GM, use the player management feature
|
||||
|
||||
## Production Deployment
|
||||
|
||||
For production builds:
|
||||
|
||||
```bash
|
||||
# Build frontend
|
||||
npm run build
|
||||
|
||||
# Start with PM2
|
||||
npm run pm2:start
|
||||
```
|
||||
|
||||
See [PM2 Configuration](./pm2.md) for details.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Port already in use
|
||||
|
||||
If port 3000 or 5000 is busy:
|
||||
|
||||
```bash
|
||||
# Find what's using the port
|
||||
lsof -i :3000
|
||||
lsof -i :5000
|
||||
|
||||
# Kill the process or change the port in .env
|
||||
```
|
||||
|
||||
### Database issues
|
||||
|
||||
The application creates the database automatically on first run. If you see errors:
|
||||
|
||||
```bash
|
||||
# Check database exists
|
||||
ls -la database/
|
||||
|
||||
# If database file is corrupted, remove it to recreate
|
||||
rm database/data.db
|
||||
```
|
||||
|
||||
### Clear cache
|
||||
|
||||
If the frontend shows old content:
|
||||
|
||||
```bash
|
||||
rm -rf .next/
|
||||
npm start
|
||||
```
|
||||
|
||||
## Next Steps
|
||||
|
||||
- Read the [User Guide](./user-guide.md) to learn features
|
||||
- Explore the [API Documentation](./api.md)
|
||||
- See [Developer Guide](./developer-guide.md) for contribution guidelines
|
||||
@@ -0,0 +1,287 @@
|
||||
# Security Guide
|
||||
|
||||
This document outlines the security measures and best practices for Deathwatch Roller.
|
||||
|
||||
## Authentication
|
||||
|
||||
### Session Management
|
||||
|
||||
- Sessions use HTTP-only cookies
|
||||
- Sessions expire after inactivity
|
||||
- Passwords are hashed with bcrypt
|
||||
- XSS protection headers enabled
|
||||
|
||||
### Session Configuration
|
||||
|
||||
```javascript
|
||||
// Example session config
|
||||
session: {
|
||||
secret: process.env.SESSION_SECRET,
|
||||
resave: false,
|
||||
saveUninitialized: false,
|
||||
cookie: {
|
||||
httpOnly: true,
|
||||
secure: process.env.NODE_ENV === 'production',
|
||||
maxAge: 24 * 60 * 60 * 1000 // 24 hours
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Database Security
|
||||
|
||||
### Password Hashing
|
||||
|
||||
```typescript
|
||||
// Password hashing
|
||||
import bcrypt from 'bcrypt';
|
||||
|
||||
const hashedPassword = await bcrypt.hash(password, 12);
|
||||
const isMatch = await bcrypt.compare(inputPassword, hashedPassword);
|
||||
```
|
||||
|
||||
### Prepared Statements
|
||||
|
||||
All database queries use parameterized statements to prevent SQL injection:
|
||||
|
||||
```typescript
|
||||
// ✅ GOOD - Using parameterized query
|
||||
const player = await Player.findOne({
|
||||
where: { email: email },
|
||||
raw: true
|
||||
});
|
||||
|
||||
// ❌ BAD - String concatenation
|
||||
const badQuery = `SELECT * FROM players WHERE email = '${email}'`;
|
||||
```
|
||||
|
||||
## Input Validation
|
||||
|
||||
### Request Validation
|
||||
|
||||
All API requests are validated:
|
||||
|
||||
```typescript
|
||||
import { body, validationResult } from 'express-validator';
|
||||
|
||||
router.post('/players', [
|
||||
body('name').isLength({ min: 1, max: 50 }).trim(),
|
||||
body('email').isEmail().normalizeEmail(),
|
||||
body('xp').isInt({ min: 0 })
|
||||
], async (req, res) => {
|
||||
const errors = validationResult(req);
|
||||
if (!errors.isEmpty()) {
|
||||
return res.status(400).json({ errors: errors.array() });
|
||||
}
|
||||
// Process request
|
||||
});
|
||||
```
|
||||
|
||||
## CORS Configuration
|
||||
|
||||
```typescript
|
||||
// CORS setup
|
||||
import cors from 'cors';
|
||||
|
||||
app.use(cors({
|
||||
origin: process.env.ALLOWED_ORIGINS?.split(','),
|
||||
credentials: true,
|
||||
methods: ['GET', 'POST', 'PUT', 'DELETE'],
|
||||
allowedHeaders: ['Content-Type', 'Authorization']
|
||||
}));
|
||||
```
|
||||
|
||||
## Rate Limiting
|
||||
|
||||
```typescript
|
||||
// Rate limiter
|
||||
import rateLimit from 'express-rate-limit';
|
||||
|
||||
const limiter = rateLimit({
|
||||
windowMs: 15 * 60 * 1000, // 15 minutes
|
||||
max: 100, // limit each IP to 100 requests per windowMs
|
||||
message: 'Too many requests from this IP, please try again later.'
|
||||
});
|
||||
|
||||
app.use('/api/', limiter);
|
||||
```
|
||||
|
||||
## Security Headers
|
||||
|
||||
```typescript
|
||||
import helmet from 'helmet';
|
||||
|
||||
app.use(helmet());
|
||||
|
||||
// Custom headers
|
||||
app.use((req, res, next) => {
|
||||
res.setHeader('X-Content-Type-Options', 'nosniff');
|
||||
res.setHeader('X-Frame-Options', 'DENY');
|
||||
res.setHeader('X-XSS-Protection', '1; mode=block');
|
||||
next();
|
||||
});
|
||||
```
|
||||
|
||||
## Environment Variables
|
||||
|
||||
Store sensitive data in environment variables:
|
||||
|
||||
```bash
|
||||
# .env file
|
||||
SESSION_SECRET=your-secure-random-string
|
||||
X_GM_SECRET=your-gm-secret-key
|
||||
DATABASE_PASSWORD=your-database-password
|
||||
```
|
||||
|
||||
Never commit `.env` files:
|
||||
|
||||
```bash
|
||||
# .gitignore
|
||||
.env
|
||||
.env.*
|
||||
!/.env.example
|
||||
```
|
||||
|
||||
## API Security
|
||||
|
||||
### GM Authentication
|
||||
|
||||
GM endpoints require special authentication:
|
||||
|
||||
```typescript
|
||||
// GM secret header
|
||||
app.use('/api/players/gm/*', (req, res, next) => {
|
||||
const secret = req.headers['x-gm-secret'];
|
||||
if (!secret || secret !== process.env.X_GM_SECRET) {
|
||||
return res.status(403).json({ error: 'Forbidden' });
|
||||
}
|
||||
next();
|
||||
});
|
||||
```
|
||||
|
||||
### API Rate Limits
|
||||
|
||||
```typescript
|
||||
const apiLimiter = rateLimit({
|
||||
windowMs: 60 * 1000, // 1 minute
|
||||
max: 100,
|
||||
keyGenerator: (req) => req.ip
|
||||
});
|
||||
|
||||
app.use('/api/', apiLimiter);
|
||||
```
|
||||
|
||||
## File Upload Security
|
||||
|
||||
```typescript
|
||||
import multer from 'multer';
|
||||
|
||||
const upload = multer({
|
||||
storage: multer.diskStorage({
|
||||
destination: (req, file, cb) => cb(null, 'uploads/'),
|
||||
filename: (req, file, cb) => {
|
||||
const uniqueName = `${Date.now()}-${Math.random()}-${file.originalname}`;
|
||||
cb(null, uniqueName);
|
||||
}
|
||||
}),
|
||||
fileFilter: (req, file, cb) => {
|
||||
const allowedTypes = ['image/jpeg', 'image/png', 'image/gif'];
|
||||
if (allowedTypes.includes(file.mimetype)) {
|
||||
cb(null, true);
|
||||
} else {
|
||||
cb(new Error('Invalid file type'));
|
||||
}
|
||||
},
|
||||
limits: {
|
||||
fileSize: 10 * 1024 * 1024 // 10MB
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
## Database Backups
|
||||
|
||||
```bash
|
||||
# Backup script
|
||||
#!/bin/bash
|
||||
mysqldump -u user -p database > backup_$(date +%Y%m%d_%H%M%S).sql
|
||||
# Rotate old backups
|
||||
find . -name "backup_*.sql" -mtime +30 -delete
|
||||
```
|
||||
|
||||
## Monitoring
|
||||
|
||||
Set up monitoring for security events:
|
||||
|
||||
```typescript
|
||||
// Audit logging
|
||||
import auditlog from 'express-auditlog';
|
||||
|
||||
app.use(auditlog());
|
||||
```
|
||||
|
||||
## Security Checklist
|
||||
|
||||
- [ ] Session cookies are HTTP-only
|
||||
- [ ] Passwords are hashed with bcrypt
|
||||
- [ ] SQL queries use parameterized statements
|
||||
- [ ] CORS is properly configured
|
||||
- [ ] Rate limiting is enabled
|
||||
- [ ] Security headers are set
|
||||
- [ ] Environment variables are not committed
|
||||
- [ ] Input is validated on all endpoints
|
||||
- [ ] File uploads are sanitized
|
||||
- [ ] Dependencies are up to date
|
||||
|
||||
## Common Vulnerabilities
|
||||
|
||||
### SQL Injection Prevention
|
||||
|
||||
```typescript
|
||||
// ✅ SAFE
|
||||
User.findOne({ where: { email: email } });
|
||||
|
||||
// ❌ UNSAFE
|
||||
User.findAll({ where: `email = '${email}'` });
|
||||
```
|
||||
|
||||
### XSS Prevention
|
||||
|
||||
```typescript
|
||||
// ✅ SAFE - Sanitize user input
|
||||
const displayName = sanitize(userInput);
|
||||
|
||||
// ❌ UNSAFE - Direct output
|
||||
res.send(userInput);
|
||||
```
|
||||
|
||||
## Dependencies
|
||||
|
||||
Keep dependencies updated:
|
||||
|
||||
```bash
|
||||
# Check for updates
|
||||
npm outdated
|
||||
|
||||
# Update all
|
||||
npm update
|
||||
|
||||
# Update specific package
|
||||
npm update express
|
||||
```
|
||||
|
||||
## Security Audits
|
||||
|
||||
```bash
|
||||
# Run security audit
|
||||
npm audit
|
||||
|
||||
# Fix vulnerabilities
|
||||
npm audit fix
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Principle of Least Privilege**: Use minimal permissions
|
||||
2. **Defense in Depth**: Multiple security layers
|
||||
3. **Fail Securely**: Default to safe responses
|
||||
4. **Log Security Events**: Monitor for suspicious activity
|
||||
5. **Regular Audits**: Periodic security reviews
|
||||
@@ -0,0 +1,336 @@
|
||||
# Troubleshooting Guide
|
||||
|
||||
Common issues and their solutions for Deathwatch Roller.
|
||||
|
||||
## Application Won't Start
|
||||
|
||||
### Port Already in Use
|
||||
|
||||
```bash
|
||||
# Check what's using port 3000 or 5000
|
||||
lsof -i :3000
|
||||
lsof -i :5000
|
||||
|
||||
# Kill the process or change the port in .env
|
||||
kill -9 <PID>
|
||||
```
|
||||
|
||||
### Dependencies Not Installed
|
||||
|
||||
```bash
|
||||
# Reinstall dependencies
|
||||
npm install
|
||||
|
||||
# Or update existing
|
||||
npm install --save
|
||||
```
|
||||
|
||||
### Node Version Too Old
|
||||
|
||||
```bash
|
||||
# Check Node version
|
||||
node --version
|
||||
|
||||
# Requires Node.js 18+ (check package.json for version)
|
||||
|
||||
# Upgrade Node
|
||||
curl -fsSL https://deb.nodesource.com/setup_18.x | sudo -E bash -
|
||||
sudo apt-get install -y nodejs
|
||||
```
|
||||
|
||||
## Database Errors
|
||||
|
||||
### Database Not Found
|
||||
|
||||
```bash
|
||||
# Check if database directory exists
|
||||
ls -la database/
|
||||
|
||||
# The database is created automatically on first run
|
||||
# If missing, remove it to recreate
|
||||
rm database/data.db
|
||||
```
|
||||
|
||||
### Corrupted Database
|
||||
|
||||
```bash
|
||||
# Backup current database
|
||||
cp database/data.db database/data.db.backup
|
||||
|
||||
# Recreate database
|
||||
rm database/data.db
|
||||
# Application will recreate on restart
|
||||
```
|
||||
|
||||
## Frontend Issues
|
||||
|
||||
### Cache Not Updating
|
||||
|
||||
```bash
|
||||
# Clear build cache
|
||||
rm -rf .next/
|
||||
rm -rf frontend/node_modules/.cache/
|
||||
|
||||
# Rebuild
|
||||
npm run build
|
||||
npm start
|
||||
```
|
||||
|
||||
### Styles Not Loading
|
||||
|
||||
Check that `vite.config.js` or webpack is configured correctly for asset handling.
|
||||
|
||||
### Build Fails
|
||||
|
||||
```bash
|
||||
# Clean and rebuild
|
||||
rm -rf node_modules
|
||||
rm -rf frontend/node_modules
|
||||
npm install
|
||||
npm run build
|
||||
```
|
||||
|
||||
## Backend Issues
|
||||
|
||||
### Database Connection Failed
|
||||
|
||||
```bash
|
||||
# Check .env file
|
||||
cat .env
|
||||
|
||||
# Verify database path exists
|
||||
ls -la $DATABASE_PATH
|
||||
```
|
||||
|
||||
### Server Logs Show Errors
|
||||
|
||||
```bash
|
||||
# Check backend logs
|
||||
tail -f backend.log
|
||||
|
||||
# Clear logs
|
||||
> backend.log
|
||||
```
|
||||
|
||||
## Authentication Issues
|
||||
|
||||
### Cannot Login
|
||||
|
||||
- Verify `.env` has `SESSION_SECRET` set
|
||||
- Check if password is correct
|
||||
- Try password reset
|
||||
|
||||
```bash
|
||||
# Reset session secret
|
||||
NODE_ENV=development npm run server
|
||||
```
|
||||
|
||||
### Session Expires Too Soon
|
||||
|
||||
```bash
|
||||
# Check session config in server.js
|
||||
# Increase maxAge in cookie config
|
||||
```
|
||||
|
||||
## API Issues
|
||||
|
||||
### CORS Errors
|
||||
|
||||
```bash
|
||||
# Check CORS configuration in server.js
|
||||
# Ensure frontend origin is allowed
|
||||
```
|
||||
|
||||
### API Endpoints Not Working
|
||||
|
||||
```bash
|
||||
# Test API directly
|
||||
curl http://localhost:5000/api/players
|
||||
|
||||
# Check backend logs
|
||||
tail -f backend.log
|
||||
```
|
||||
|
||||
## Deployment Issues
|
||||
|
||||
### PM2 Not Starting
|
||||
|
||||
```bash
|
||||
# Check ecosystem.config.js exists
|
||||
ls ecosystem.config.js
|
||||
|
||||
# View PM2 logs
|
||||
pm2 logs
|
||||
|
||||
# Restart apps
|
||||
pm2 restart all
|
||||
```
|
||||
|
||||
### Build Fails in Production
|
||||
|
||||
```bash
|
||||
# Clean build
|
||||
npm run clean
|
||||
npm run build
|
||||
|
||||
# Check for build errors
|
||||
npm run build 2>&1
|
||||
```
|
||||
|
||||
## Performance Issues
|
||||
|
||||
### Application is Slow
|
||||
|
||||
```bash
|
||||
# Check for memory leaks
|
||||
pm2 monitortype --interval 10000
|
||||
|
||||
# Check database queries
|
||||
# Add indexes to frequently queried fields
|
||||
|
||||
# Enable compression
|
||||
npm install compression
|
||||
app.use(compression());
|
||||
```
|
||||
|
||||
### High CPU Usage
|
||||
|
||||
```bash
|
||||
# Identify the process
|
||||
ps aux | grep node
|
||||
|
||||
# Check for infinite loops in code
|
||||
# Review recent deployments
|
||||
```
|
||||
|
||||
## Browser Issues
|
||||
|
||||
### Styles Not Applying
|
||||
|
||||
- Clear browser cache (Ctrl+Shift+Delete)
|
||||
- Try incognito mode
|
||||
- Check browser console for errors
|
||||
|
||||
### JavaScript Console Errors
|
||||
|
||||
```bash
|
||||
# Check frontend code for syntax errors
|
||||
cd frontend
|
||||
npm run lint
|
||||
|
||||
# Check browser DevTools
|
||||
# Network tab for failed requests
|
||||
```
|
||||
|
||||
## Production Issues
|
||||
|
||||
### Application Crashes on Restart
|
||||
|
||||
```bash
|
||||
# Check error logs
|
||||
pm2 logs --err
|
||||
|
||||
# Enable better error logging
|
||||
NODE_ENV=production npm run start
|
||||
```
|
||||
|
||||
### Memory Issues
|
||||
|
||||
```bash
|
||||
# Monitor memory usage
|
||||
pm2 monitortype --interval 10000
|
||||
|
||||
# Increase max_memory_restart in ecosystem.config.js
|
||||
max_memory_restart: '1G'
|
||||
```
|
||||
|
||||
## Development Issues
|
||||
|
||||
### TS Errors
|
||||
|
||||
```bash
|
||||
# Type check
|
||||
cd backend
|
||||
npx tsc --noEmit
|
||||
|
||||
# Fix errors in .ts files
|
||||
```
|
||||
|
||||
### E2E Tests Failing
|
||||
|
||||
```bash
|
||||
# Run tests in debug mode
|
||||
npm run test:e2e -- --debug
|
||||
|
||||
# Check test logs
|
||||
npm run test:e2e
|
||||
```
|
||||
|
||||
## Quick Fixes
|
||||
|
||||
### Reset Everything
|
||||
|
||||
```bash
|
||||
# Stop all services
|
||||
pm2 stop all
|
||||
|
||||
# Clear caches
|
||||
rm -rf .next/
|
||||
rm -rf frontend/node_modules/.cache/
|
||||
|
||||
# Recreate database
|
||||
rm database/data.db
|
||||
|
||||
# Reinstall
|
||||
npm install
|
||||
|
||||
# Restart
|
||||
npm run pm2:start
|
||||
```
|
||||
|
||||
### Debug Mode
|
||||
|
||||
```bash
|
||||
# Enable verbose logging
|
||||
NODE_ENV=development npm run dev
|
||||
|
||||
# Watch mode
|
||||
npm run dev
|
||||
```
|
||||
|
||||
## Getting Help
|
||||
|
||||
1. Check the logs: `tail -f backend.log`
|
||||
2. Review the [Developer Guide](./developer-guide.md)
|
||||
3. Search for issues on GitHub
|
||||
4. Create a new issue with:
|
||||
- Steps to reproduce
|
||||
- Expected vs actual behavior
|
||||
- Logs and error messages
|
||||
|
||||
## Common Error Messages
|
||||
|
||||
### "Cannot find module"
|
||||
|
||||
```bash
|
||||
npm install
|
||||
# or
|
||||
npm install --save <missing-package>
|
||||
```
|
||||
|
||||
### "EADDRINUSE"
|
||||
|
||||
```bash
|
||||
# Check what's using the port
|
||||
lsof -i :<port>
|
||||
kill -9 <PID>
|
||||
```
|
||||
|
||||
### "Database connection failed"
|
||||
|
||||
```bash
|
||||
# Check .env has correct database path
|
||||
cat .env | grep DATABASE_PATH
|
||||
# Or set it explicitly
|
||||
DATABASE_PATH=./database/data.db
|
||||
```
|
||||
@@ -0,0 +1,118 @@
|
||||
# User Guide
|
||||
|
||||
## Introduction
|
||||
|
||||
Deathwatch Roller helps you manage your tabletop RPG sessions, track player progress, and access game resources.
|
||||
|
||||
## Main Features
|
||||
|
||||
### Dashboard
|
||||
|
||||
The main page provides quick access to all features:
|
||||
- Shop for items
|
||||
- Browse bestiary
|
||||
- View rules database
|
||||
- Access GM tools (if authorized)
|
||||
|
||||
### Shop
|
||||
|
||||
Browse and purchase equipment:
|
||||
|
||||
1. Click **"Shop"** in the navigation
|
||||
2. Filter by item type (weapons, armor, vehicles)
|
||||
3. Check RP cost and stats
|
||||
4. Click **"Purchase"** to buy items
|
||||
5. Items are added to your requisition
|
||||
|
||||
### Bestiary
|
||||
|
||||
Look up monsters and enemies:
|
||||
|
||||
1. Click **"Bestiary"** in navigation
|
||||
2. Search by name or type
|
||||
3. View monster stats and weaknesses
|
||||
4. Use in combat preparation
|
||||
|
||||
### Rules Database
|
||||
|
||||
Access game rules:
|
||||
|
||||
1. Click **"Rules"** in navigation
|
||||
2. Search for specific rules
|
||||
3. View quick reference cards
|
||||
4. Print rulesheets for reference
|
||||
|
||||
### Player Management (GM Only)
|
||||
|
||||
Game Masters can manage players:
|
||||
|
||||
1. Click **"Players"** in navigation (GM only)
|
||||
2. Create new player accounts
|
||||
3. Edit player stats and RP
|
||||
4. Reset passwords
|
||||
5. Bulk modify XP/RP
|
||||
|
||||
### Session Tracking
|
||||
|
||||
Track your game sessions:
|
||||
|
||||
1. Click **"Sessions"** in navigation
|
||||
2. Create a new session
|
||||
3. Add player notes
|
||||
4. Track accomplishments
|
||||
5. Export session data
|
||||
|
||||
## Tips and Tricks
|
||||
|
||||
### Quick Reference
|
||||
|
||||
Print quick reference cards for your next session:
|
||||
- Click **"Print"** in navigation
|
||||
- Download PDF reference sheets
|
||||
- Keep handy for quick lookup
|
||||
|
||||
### GM Kit
|
||||
|
||||
Game Masters have access to special tools:
|
||||
- Character generator
|
||||
- Scenario templates
|
||||
- Combat generator
|
||||
- Utility scripts
|
||||
|
||||
### Session Organization
|
||||
|
||||
Organize your sessions:
|
||||
- Add notes after each game
|
||||
- Track player progression
|
||||
- Maintain a campaign log
|
||||
- Export data for backups
|
||||
|
||||
## Keyboard Shortcuts
|
||||
|
||||
| Shortcut | Action |
|
||||
|----------|--------|
|
||||
| `Ctrl+K` | Quick search |
|
||||
| `Ctrl+S` | Save current view |
|
||||
| `Ctrl+P` | Print current page |
|
||||
|
||||
## Accessibility
|
||||
|
||||
The application supports:
|
||||
- Screen readers
|
||||
- Keyboard navigation
|
||||
- High contrast mode
|
||||
- Adjustable text size
|
||||
|
||||
## Privacy
|
||||
|
||||
- Player data is stored locally
|
||||
- No external data collection
|
||||
- Session data is encrypted
|
||||
- GM tools require secret key
|
||||
|
||||
## Support
|
||||
|
||||
For issues or questions:
|
||||
- Check the [Developer Guide](./developer-guide.md)
|
||||
- Review the [API documentation](./api.md)
|
||||
- See open issues on GitHub
|
||||
Reference in New Issue
Block a user