- 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>
440 lines
7.9 KiB
Markdown
440 lines
7.9 KiB
Markdown
# 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)
|