- 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 <noreply@anthropic.com>
7.9 KiB
7.9 KiB
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
cd backend
npm install
npm run dev
The backend starts on port 5000.
Frontend
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
// 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
// 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 accountsitems- Shop inventoryinventory- Player item ownershipsessions- Game session trackingwebhooks- Webhook configurations
Migrations
Migrations are stored in backend/migrations/.
# Run migrations
npm run migrate
# Create new migration
npx knex migrate:make create_new_table
API Development
Creating a New Endpoint
backend/src/routes/new-endpoints.ts
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:
import newEndpoints from './src/routes/new-endpoints';
app.use('/api/new', newEndpoints);
Adding Authentication
import { requireAuth } from '../middleware/auth';
router.get(protectedRoute, requireAuth, async (req, res) => {
// Protected route logic
});
Frontend Development
Creating a New Component
frontend/src/components/NewComponent.tsx
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:
// 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
backend/src/routes/__tests__/new-endpoints.test.ts
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:
npm test
Frontend Tests
frontend/src/components/__tests__/NewComponent.test.tsx
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:
npm test
Debugging
Backend Debugging
# Enable verbose logging
NODE_ENV=development npm run server
# View logs
tail -f backend.log
Frontend Debugging
# Open DevTools
npm run dev
# Check browser console
# Network tab for API calls
Deployment
Build
# Build frontend
npm run build
# Build backend
cd backend
npm run build
PM2 Setup
# Start with PM2
npm run pm2:start
# View logs
pm2 logs
# Stop
npm run pm2:stop
Production Environment Variables
# Create production env
cp .env.example .env.production
# Set required variables:
# - NODE_ENV=production
# - SESSION_SECRET
# - X_GM_SECRET
# - PORT
Performance
Optimization Tips
- Database: Use indexes for frequent queries
- Frontend: Enable code splitting
- API: Implement caching for static data
- Images: Use lazy loading
Caching
// 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:
// 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
- Use HTTPS in production
- Rate limit API requests
- Validate all inputs
- Use prepared statements for SQL
- Never expose sensitive data
Contributing
Pull Request Process
- Fork the repository
- Create a feature branch
- Make your changes
- Add tests for new functionality
- Update documentation
- Submit a pull request
Code Review
All PRs will be reviewed for:
- Code quality
- Test coverage
- Documentation
- Security
- Performance
Commit Messages
Use conventional commits:
feat: add new shop item filter
fix: resolve authentication issue
docs: update API documentation
chore: update dependencies